home *** CD-ROM | disk | FTP | other *** search
/ AmigActive 10 / AACD 10.iso / AACD / Resources / Online / Term / Extras / Source / term-source.lha / zmodem.c < prev   
C/C++ Source or Header  |  1996-10-20  |  87KB  |  3,852 lines

  1. /*
  2. **  ZModem.c
  3. **
  4. **  Built-in ZModem file transfer protocol.
  5. **
  6. **  :ts=4
  7. **
  8. **  Based upon the zmodem.c code used by MacTerminal, written by
  9. **
  10. **      Erny Tontlinger
  11. **      33, route d'Arlon
  12. **      L-8410 Steinfort
  13. **      Luxembourg
  14. **
  15. **  Which in turn is based upon work by
  16. **
  17. **      Chuck Forsberg
  18. **      Omen Technology, Inc
  19. **      Post Office Box 4681
  20. **      Portland OR 97208
  21. **      USA
  22. **
  23. **  All modifications for `term', great and small, are
  24. **  copyright © 1990-1996 by Olaf `Olsen' Barthel
  25. **      All Rights Reserved
  26. */
  27.  
  28. #ifdef BUILTIN_ZMODEM
  29.  
  30. #ifndef _GLOBAL_H
  31. #include "Global.h"
  32. #endif
  33.  
  34. #include <setjmp.h>
  35.  
  36. #ifdef FINE
  37. #undef FINE
  38. #endif /* FINE */
  39.  
  40. #ifdef XON
  41. #undef XON
  42. #endif /* XON */
  43.  
  44. #ifdef CAN
  45. #undef CAN
  46. #endif /* CAN */
  47.  
  48. #ifdef DEL
  49. #undef DEL
  50. #endif /* DEL */
  51.  
  52. enum
  53. {
  54.     GAD_NameBox=1000,
  55.     GAD_BatchBox,
  56.     GAD_MessageList,
  57.     GAD_TransferBox,
  58.     GAD_FileProgress,
  59.     GAD_BatchProgress,
  60.     GAD_Abort
  61. };
  62.  
  63. enum
  64. {
  65.     NAMEBOX_Name,
  66.     NAMEBOX_Size,
  67.     NAMEBOX_Completion
  68. };
  69.  
  70. enum
  71. {
  72.     BATCHBOX_Count,
  73.     BATCHBOX_Size,
  74.     BATCHBOX_Completion
  75. };
  76.  
  77. enum
  78. {
  79.     TRANSFERBOX_Errors,
  80.     TRANSFERBOX_Retries,
  81.     TRANSFERBOX_CPS
  82. };
  83.  
  84. enum
  85. {
  86.   FINE = 0,                     /* No error */
  87.   NOTHING = 1000,               /* No data available */
  88.   TIMEOUT,                      /* Timeout error */
  89.   CANCEL,                       /* Cancel by button or menu */
  90.   ABORT,                        /* Abort by ctrl-X received */
  91.   ERROR,                        /* Other error */
  92.   NO_CARRIER,                   /* Carrier lost */
  93.   LINE_ERROR,
  94.   PARITY_ERROR,
  95.   TIMER_ERROR,
  96.   BUFFER_OVERFLOW,
  97.   NO_DSR,
  98.   BREAK_DETECTED
  99. };
  100.  
  101. STATIC BOOL ZEscapeCtl = FALSE;                 /* escape all control characters. */
  102. STATIC BOOL ZOverwrite = FALSE;                 /* clobber existing files. */
  103. STATIC LONG ZTimeout = 10;                      /* receive timeout (seconds) */
  104. STATIC LONG ZBuffer = 0;                        /* receive buffer size (bytes) */
  105. STATIC LONG ZRetries = 10;                      /* receive maximum retries */
  106. STATIC LONG ZErrors = 10;                       /* maximum errors */
  107. STATIC LONG ZPacket = 1024;                     /* transmit sub-packet length (bytes) */
  108. STATIC LONG ZWindow = 0;                        /* transmit window size (bytes) */
  109. STATIC LONG Zcrcq = 0;                          /* transmit ZCRCQ spacing (bytes) */
  110. STATIC BOOL ZKeepPartial = TRUE;                /* keep partial files; do not delete them */
  111. STATIC BOOL ZDeleteAfterSending = FALSE;        /* delete the file after sending */
  112. STATIC BOOL ZProtectAfterSending = FALSE;       /* add archived bit after sending */
  113. STATIC BOOL ZSkipIfArchived = FALSE;            /* skip file if archived bit is set */
  114. STATIC BOOL ZSendFullPath = FALSE;              /* send complete file path */
  115. STATIC BOOL ZUseFullPath = FALSE;               /* use complete file path */
  116. STATIC BOOL ZTransferIfNewer = FALSE;           /* transfer file if newer than existing file */
  117. STATIC BOOL ZTransferIfSize = FALSE;            /* transfer file if size different from existing file */
  118.  
  119. /* ----- Special characters -------------------------------------------- */
  120.  
  121. #define BS          0x08        /* Backspace */
  122. #define LF          0x0A        /* Linefeed */
  123. #define CR          0x0D        /* Carriage return */
  124. #define DLE         0x10        /* Data link escape */
  125. #define XON         0x11        /* Flow control */
  126. #define XOFF        0x13        /* Flow control */
  127. #define CAN         0x18        /* Cancel */
  128. #define DEL         0x7F        /* Delete */
  129.  
  130. #define ZPAD        '*'         /* Padding character begins frames */
  131. #define ZDLE        ('X'&0x1F)  /* Zmodem escape character ctrl-X */
  132. #define ZBIN        'A'         /* Binary header indicator (CRC-16) */
  133. #define ZHEX        'B'         /* HEX header indicator */
  134.  
  135. /* ----- Frame types --------------------------------------------------- */
  136.  
  137. #define ZRQINIT     0           /* Request receive init */
  138. #define ZRINIT      1           /* Receive init */
  139. #define ZSINIT      2           /* Send init sequence (optional) */
  140. #define ZACK        3           /* ACK to above */
  141. #define ZFILE       4           /* File name from sender */
  142. #define ZSKIP       5           /* To sender: skip this file */
  143. #define ZNAK        6           /* Last packet was garbled */
  144. #define ZABORT      7           /* Abort batch transfers */
  145. #define ZFIN        8           /* Finish session */
  146. #define ZRPOS       9           /* Resume data trans at this position */
  147. #define ZDATA       10          /* Data packet(s) follow */
  148. #define ZEOF        11          /* End of file */
  149. #define ZFERR       12          /* Fatal Read or Write error Detected */
  150. #define ZCRC        13          /* Request for file CRC and response */
  151. #define ZCHALLENGE  14          /* Receiver's Challenge */
  152. #define ZCOMPL      15          /* Request is complete */
  153. #define ZCAN        16          /* Other end canned session with 5 CANs */
  154. #define ZFREECNT    17          /* Request for free bytes on filesystem */
  155. #define ZCOMMAND    18          /* Command from sending program */
  156. #define ZSTDERR     19          /* Output to standard error, data follows */
  157.  
  158. /* ----- ZDLE sequences ------------------------------------------------ */
  159.  
  160. #define ZCRCE       'h'         /* CRC next, frame ends, header follows */
  161. #define ZCRCG       'i'         /* CRC next, frame continues nonstop */
  162. #define ZCRCQ       'j'         /* CRC next, frame continues, ZACK expected */
  163. #define ZCRCW       'k'         /* CRC next, ZACK expected, end of frame */
  164. #define ZRUB0       'l'         /* Translate to DEL 0x7F */
  165. #define ZRUB1       'm'         /* Translate to DEL 0xFF */
  166.  
  167. /* ----- Header structures --------------------------------------------- */
  168.  
  169. /* olsen: neat idea, but assumes that bit fields will be allocated
  170.  *        from left to right. Luckily, that's just what SAS/C 6.50 does.
  171.  */
  172.  
  173. typedef struct
  174. {
  175.   unsigned int pad:24;
  176.   unsigned int command:8;       /* 0 or ZCOMMAND */
  177. }
  178. ZRQINITflags;
  179.  
  180. typedef struct
  181. {
  182.   unsigned int bufsize:16;      /* Receiver's buffer size (bytes swapped) */
  183.  
  184.   unsigned int pad:7;
  185.   unsigned int canvhdr:1;       /* Variable headers OK */
  186.  
  187.   unsigned int esc8:1;          /* Rx expects 8th bit to be escaped */
  188.   unsigned int escctl:1;        /* Rx expects ctl chars to be escaped */
  189.   unsigned int canfc32:1;       /* Rx can use 32 bit CRC */
  190.   unsigned int canlzw:1;        /* Rx can uncompress */
  191.   unsigned int canrle:1;        /* Rx can decode RLE */
  192.   unsigned int canbrk:1;        /* Rx can send a break signal */
  193.   unsigned int canovio:1;       /* Rx can receive data during disk I/O */
  194.   unsigned int canfdx:1;        /* Rx can send and receive true FDX */
  195. }
  196. ZRINITflags;
  197.  
  198. typedef struct
  199. {
  200.   unsigned int pad0:24;
  201.  
  202.   unsigned int esc8:1;          /* Tx expects 8th bit to be escaped */
  203.   unsigned int escctl:1;        /* Tx expects ctl chars to be escaped */
  204.   unsigned int pad1:6;
  205. }
  206. ZSINITflags;
  207.  
  208. typedef struct
  209. {
  210.   unsigned int pad:1;
  211.   unsigned int sparse:1;        /* Encoding for sparse file operations */
  212.   unsigned int canvhdr:6;       /* Variable headers OK */
  213.  
  214.   unsigned int transport:8;     /* Transport options */
  215.  
  216.   unsigned int skip:1;          /* Skip file if not present at rx */
  217.   unsigned int manage:7;        /* Management options */
  218.  
  219.   unsigned int conv:8;          /* Conversion options */
  220. }
  221. ZFILEflags;
  222.  
  223. typedef struct
  224. {
  225.   unsigned int pad:31;
  226.   unsigned int ack:1;           /* Acknowledge, then do command */
  227. }
  228. ZCOMMANDflags;
  229.  
  230. typedef union
  231. {
  232.   UBYTE b[4];
  233.   ULONG position;               /* File position (low byte first) */
  234.   ZRQINITflags zrqinit;
  235.   ZRINITflags zrinit;
  236.   ZSINITflags zsinit;
  237.   ZFILEflags zfile;
  238.   ZCOMMANDflags zcommand;
  239. }
  240. HEADER;
  241.  
  242. /* ----- Conversion options, ZFILE frame ------------------------------- */
  243.  
  244. #define ZCBIN       1           /* Binary transfer - inhibit conversion */
  245. #define ZCNL        2           /* Convert NL to local eol convention */
  246. #define ZCRESUM     3           /* Resume interrupted file transfer */
  247.  
  248. /* ----- Management options, ZFILE frame ------------------------------- */
  249.  
  250. #define ZMNEWL      1           /* Transfer if source newer or longer */
  251. #define ZMCRC       2           /* Transfer if different file CRC or length */
  252. #define ZMAPND      3           /* Append contents to existing file (if any) */
  253. #define ZMCLOB      4           /* Replace existing file */
  254. #define ZMNEW       5           /* Transfer if source newer */
  255. #define ZMDIFF      6           /* Transfer if dates or lengths different */
  256. #define ZMPROT      7           /* Protect destination file */
  257.  
  258. /* ----- Transport options, ZFILE frame -------------------------------- */
  259.  
  260. #define ZTLZW       1           /* Lempel-Ziv compression */
  261. #define ZTRLE       3           /* Run Length encoding */
  262.  
  263. /* ----- Other constants ----------------------------------------------- */
  264.  
  265. #define ZATTNLEN    32          /* Max length of attention string */
  266. #define ZMAXSPLEN   1024        /* Max subpacket length */
  267.  
  268. /* ----- Return values (internal) -------------------------------------- */
  269.  
  270. #define GOTOR   0x0100
  271. #define GOTCRCE (GOTOR | ZCRCE) /* ZDLE-ZCRCE received */
  272. #define GOTCRCG (GOTOR | ZCRCG) /* ZDLE-ZCRCG received */
  273. #define GOTCRCQ (GOTOR | ZCRCQ) /* ZDLE-ZCRCQ received */
  274. #define GOTCRCW (GOTOR | ZCRCW) /* ZDLE-ZCRCW received */
  275.  
  276. /* ----- Globals ------------------------------------------------------- */
  277.  
  278. UBYTE ZAutoReceiveString[] =
  279. {
  280.   ZPAD, ZDLE, ZHEX,
  281.   '0', '0',                     /* Type: ZRQINIT */
  282.   '0', '0', '0', '0', '0', '0', '0', '0',
  283.   '0', '0', '0', '0',           /* CRC1 CRC2 */
  284.   CR,
  285.   0
  286. };
  287.  
  288. UBYTE ZLastRx[] =
  289. {
  290.   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
  291. };
  292.  
  293. #define GARBAGE 1400+2400       /* Garbage count before header */
  294.  
  295. STATIC jmp_buf CancelEnv;       /* Long jump if cancel or abort */
  296.  
  297. STATIC BOOL Zctlesc;            /* TRUE: escape all control characters */
  298. STATIC UBYTE *TxBuffer;         /* Transmit buffer */
  299. STATIC UBYTE *TxPtr;            /* Pointer into transmit buffer */
  300.  
  301. /* Shared by transmit and receive */
  302. STATIC BPTR FileHandle;         /* File handle */
  303. STATIC UBYTE FileName[MAX_FILENAME_LENGTH];     /* Current file name */
  304. STATIC ULONG Mark;              /* Current file position */
  305. STATIC ULONG Length;            /* Length of file */
  306. STATIC ULONG StartPos;          /* Start file position */
  307. STATIC ULONG StartupTime;       /* Start time */
  308. STATIC ULONG ZBytesOut, ZBytesIn; /* Number of bytes sent/received */
  309. STATIC UBYTE *Buffer;           /* Block buffer + attention string + ... */
  310. STATIC ULONG LastBPS;           /* Throughput (bytes/second) */
  311.  
  312. #define SERIALBUFFERSIZE (2 * ZPacket)
  313. STATIC UBYTE *SerialBuffer;
  314. STATIC UBYTE *SerialIndex;
  315. STATIC LONG SerialCache;
  316.  
  317. /* Transmit only */
  318. STATIC ULONG Txwindow;          /* Size of the transmitted window */
  319. STATIC ULONG Txwspac;           /* Spacing between ZCRCQ requests */
  320. STATIC UWORD Rxbuflen;          /* Receiver's max buffer length */
  321. STATIC ULONG Lastsync;          /* Last offset to which we got a ZRPOS */
  322. STATIC ULONG Lrxpos;            /* Receiver's last reported offset */
  323. STATIC WORD BeenHereB4;         /* How many times ZRPOS same place */
  324.  
  325. /* Receive only */
  326. STATIC UBYTE TryZhdrType;       /* Header type to send */
  327. STATIC UWORD Count;             /* Bytes in block buffer */
  328.  
  329. #define Attn (Buffer+ZMAXSPLEN) /* Sender's attention string */
  330. STATIC UWORD AttnLen;           /* Bytes in attention string */
  331.  
  332. #define BUFFERSIZE (ZMAXSPLEN+ZATTNLEN)
  333. STATIC BOOL ConvertNL;          /* Convert newline to local eol */
  334.  
  335. STATIC UBYTE zc;
  336. STATIC BPTR ReceiveDir;
  337. STATIC BOOL IsBlockMapped;
  338. STATIC LONG errors;
  339. STATIC LayoutHandle *handle;
  340. STATIC struct List *MessageList;
  341. STATIC LONG MessageListLen;
  342. STATIC BOOL Transmitting;
  343. STATIC ULONG BytesThisBatch,FilesThisBatch;
  344.  
  345. /* ===== LOCAL PROTOTYPES=============================================== */
  346. STATIC ULONG Unix2AmigaMode(ULONG Unix);
  347. STATIC ULONG Amiga2UnixMode(ULONG Amiga);
  348. STATIC LONG MapSerialError(LONG err);
  349. STATIC VOID CloseUI(LayoutHandle *handle, LONG err);
  350. STATIC VOID AddMessage(STRPTR Message, ...);
  351. STATIC VOID ResetUI(LayoutHandle *handle);
  352. STATIC LayoutHandle *OpenUI(STRPTR Title);
  353. STATIC ULONG CalculateBPS(VOID);
  354. STATIC VOID UpdateError(LONG error);
  355. STATIC VOID NameProgress(STRPTR name);
  356. STATIC BOOL CheckCancel(VOID);
  357. STATIC VOID UpdateRetries(LONG retries);
  358. STATIC VOID UpdateBatch(ULONG number, ULONG numberLeft, ULONG bytes, ULONG bytesLeft);
  359. STATIC VOID UpdateProgress(LONG bytes, LONG maximum);
  360. STATIC VOID ToOctal(ULONG v, UBYTE *s);
  361. STATIC ULONG GetTime(VOID);
  362. STATIC BOOL Escape(VOID);
  363. STATIC LONG NewTxBuffer(VOID);
  364. STATIC VOID DisposeTxBuffer(VOID);
  365. STATIC VOID InitTxBuffer(VOID);
  366. STATIC VOID TransmitTxBuffer(VOID);
  367. STATIC VOID SendString(UBYTE *s, UWORD n);
  368. STATIC VOID SendCancel(VOID);
  369. STATIC UBYTE SendByte(UBYTE c);
  370. STATIC VOID SendAttn(UBYTE *s);
  371. STATIC UWORD __inline Swap2(UWORD n);
  372. STATIC ULONG __inline Swap4(ULONG n);
  373. STATIC VOID AdjustHeader(UBYTE type, HEADER *p);
  374. STATIC UBYTE PutHex(UBYTE c);
  375. STATIC UBYTE LocalSendLine(UBYTE c);
  376. STATIC VOID SendHexHeader(UBYTE type, HEADER *hdr);
  377. STATIC VOID SendBinaryHeader(UBYTE type, HEADER *hdr);
  378. STATIC VOID SendData(UBYTE *buf, UWORD length, UBYTE frameend);
  379. STATIC WORD ReadByte(UBYTE *c, ULONG timeout);
  380. STATIC WORD WaitZPAD(ULONG timeout, UWORD n);
  381. STATIC WORD ReadByte2(UBYTE *c, ULONG timeout);
  382. STATIC WORD GetHex(UBYTE *c);
  383. STATIC WORD ZdleRead(UBYTE *c);
  384. STATIC WORD ReceiveBinaryHeader(UBYTE *type, UBYTE *hdr);
  385. STATIC WORD ReceiveHexHeader(UBYTE *type, UBYTE *hdr);
  386. STATIC WORD GetHeader(UBYTE *type, HEADER *hdr);
  387. STATIC WORD ReceiveData(UBYTE *buf, UWORD length, UWORD *count, UBYTE *framend);
  388. STATIC VOID ConvertTime(BOOL toAmiga, ULONG *t);
  389. STATIC LONG LocalReadBuffer(LONG *count, UBYTE *buf);
  390. STATIC LONG LocalReadBufferAndConvert(LONG *count, UBYTE *buf, UBYTE *lastc);
  391. STATIC VOID SetFilePosition(ULONG position);
  392. STATIC WORD GetInSync(BOOL flag, UBYTE *type);
  393. STATIC WORD SendFileData(VOID);
  394. STATIC WORD Invitation(VOID);
  395. STATIC WORD SessionStartup(VOID);
  396. STATIC WORD SendFileInfo(UBYTE *name, ULONG size, BOOL text, ULONG modif, ULONG mode, ULONG *startpos, ULONG TotalFiles, ULONG TotalBytes);
  397. STATIC VOID SessionCleanup(VOID);
  398. STATIC ULONG GetFreeSpace(VOID);
  399. STATIC VOID AckOverAndOut(VOID);
  400. STATIC WORD TryZReceive(HEADER *filehdr);
  401. STATIC LONG ProcedeHeader(HEADER *filehdr, STRPTR name, ULONG *length, ULONG *modif, ULONG *mode, ULONG *filesleft, ULONG *bytesleft);
  402. STATIC LONG WriteFile(VOID);
  403. STATIC VOID CleanupFile(STRPTR name, ULONG modif, ULONG mode);
  404. STATIC WORD ReceiveFile(HEADER *filehdr);
  405. STATIC WORD ReceiveFiles(VOID);
  406.  
  407. /* ===== FILE MODE HANDLING ============================================ */
  408. #define S_IRWXU 0000700         /* RWX mask for owner */
  409. #define S_IRUSR 0000400         /* R for owner */
  410. #define S_IWUSR 0000200         /* W for owner */
  411. #define S_IXUSR 0000100         /* X for owner */
  412.  
  413. #define S_IRWXG 0000070         /* RWX mask for group */
  414. #define S_IRGRP 0000040         /* R for group */
  415. #define S_IWGRP 0000020         /* W for group */
  416. #define S_IXGRP 0000010         /* X for group */
  417.  
  418. #define S_IRWXO 0000007         /* RWX mask for other */
  419. #define S_IROTH 0000004         /* R for other */
  420. #define S_IWOTH 0000002         /* W for other */
  421. #define S_IXOTH 0000001         /* X for other */
  422.  
  423. #define S_IFMT  0170000         /* type of file */
  424. #define S_IFREG 0100000         /* regular */
  425.  
  426. STATIC ULONG
  427. Unix2AmigaMode (ULONG Unix)
  428. {
  429.   if (Unix & S_IFMT)
  430.   {
  431.     ULONG Amiga;
  432.  
  433.     Amiga = 0;
  434.  
  435.     if (Unix & S_IXOTH)
  436.       Amiga |= FIBF_OTR_EXECUTE;
  437.  
  438.     if (Unix & S_IWOTH)
  439.       Amiga |= FIBF_OTR_WRITE | FIBF_OTR_DELETE;
  440.  
  441.     if (Unix & S_IROTH)
  442.       Amiga |= FIBF_OTR_READ;
  443.  
  444.  
  445.     if (Unix & S_IXGRP)
  446.       Amiga |= FIBF_GRP_EXECUTE;
  447.  
  448.     if (Unix & S_IWGRP)
  449.       Amiga |= FIBF_GRP_WRITE | FIBF_GRP_DELETE;
  450.  
  451.     if (Unix & S_IRGRP)
  452.       Amiga |= FIBF_GRP_READ;
  453.  
  454.  
  455.     if (Unix & S_IXUSR)
  456.       Amiga |= FIBF_EXECUTE;
  457.  
  458.     if (Unix & S_IWUSR)
  459.       Amiga |= FIBF_WRITE | FIBF_DELETE;
  460.  
  461.     if (Unix & S_IRUSR)
  462.       Amiga |= FIBF_READ;
  463.  
  464.     return (Amiga ^ (FIBF_EXECUTE | FIBF_WRITE | FIBF_DELETE | FIBF_READ));
  465.   }
  466.   else
  467.     return (0);
  468. }
  469.  
  470. STATIC ULONG
  471. Amiga2UnixMode (ULONG Amiga)
  472. {
  473.   ULONG Unix;
  474.  
  475.   Amiga ^= FIBF_EXECUTE | FIBF_WRITE | FIBF_DELETE | FIBF_READ;
  476.  
  477.   Unix = S_IFREG;
  478.  
  479.   if (Amiga & FIBF_OTR_EXECUTE)
  480.     Unix |= S_IXOTH;
  481.  
  482.   if (Amiga & (FIBF_OTR_WRITE | FIBF_OTR_DELETE))
  483.     Unix |= S_IWOTH;
  484.  
  485.   if (Amiga & FIBF_OTR_READ)
  486.     Unix |= S_IROTH;
  487.  
  488.  
  489.   if (Amiga & FIBF_GRP_EXECUTE)
  490.     Unix |= S_IXGRP;
  491.  
  492.   if (Amiga & (FIBF_GRP_WRITE | FIBF_GRP_DELETE))
  493.     Unix |= S_IWGRP;
  494.  
  495.   if (Amiga & FIBF_GRP_READ)
  496.     Unix |= S_IRGRP;
  497.  
  498.  
  499.   if (Amiga & FIBF_EXECUTE)
  500.     Unix |= S_IXUSR;
  501.  
  502.   if (Amiga & (FIBF_WRITE | FIBF_DELETE))
  503.     Unix |= S_IWUSR;
  504.  
  505.   if (Amiga & FIBF_READ)
  506.     Unix |= S_IRUSR;
  507.  
  508.   return (Unix);
  509. }
  510.  
  511. /* ===== MISC ========================================================== */
  512.  
  513. STATIC LONG
  514. MapSerialError (LONG err)
  515. {
  516.   switch (err)
  517.   {
  518.   case SerErr_LineErr:
  519.  
  520.     err = LINE_ERROR;
  521.     break;
  522.  
  523.   case SerErr_ParityErr:
  524.  
  525.     err = PARITY_ERROR;
  526.     break;
  527.  
  528.   case SerErr_TimerErr:
  529.  
  530.     err = TIMER_ERROR;
  531.     break;
  532.  
  533.   case SerErr_BufOverflow:
  534.  
  535.     err = BUFFER_OVERFLOW;
  536.     break;
  537.  
  538.   case SerErr_NoDSR:
  539.  
  540.     err = NO_DSR;
  541.     break;
  542.  
  543.   case SerErr_DetectedBreak:
  544.  
  545.     err = BREAK_DETECTED;
  546.     break;
  547.  
  548.   default:
  549.  
  550.     err = 0;
  551.     break;
  552.   }
  553.  
  554.   return (err);
  555. }
  556.  
  557. STATIC VOID
  558. CloseUI (LayoutHandle *handle, LONG err)
  559. {
  560.   if (err > 0)
  561.   {
  562.     LT_ShowWindow(handle,TRUE);
  563.     DisplayBeep (handle->Window->WScreen);
  564.  
  565.     do
  566.       WaitPort(handle->Window->UserPort);
  567.     while(!CheckCancel());
  568.   }
  569.  
  570.   LT_DeleteHandle(handle);
  571.  
  572.   DeleteList(MessageList);
  573.   MessageList = NULL;
  574. }
  575.  
  576. STATIC VOID
  577. AddMessage(STRPTR Message,...)
  578. {
  579.     struct Node *Node;
  580.     va_list VarArgs;
  581.     ULONG Size;
  582.  
  583.     va_start(VarArgs,Message);
  584.     Size = GetFormatLength(Message,VarArgs);
  585.  
  586.     LT_SetAttributes(handle,
  587.         GTLV_Labels,~0,
  588.     TAG_DONE);
  589.  
  590.     if(MessageListLen == 50)
  591.     {
  592.         FreeVecPooled(RemHead(MessageList));
  593.         MessageListLen--;
  594.     }
  595.  
  596.     if(Node = (struct Node *)AllocVecPooled(sizeof(struct Node) + Count,MEMF_ANY))
  597.     {
  598.         VSPrintf(Node->ln_Name = (char *)(Node + 1),Message,VarArgs);
  599.         AddTail(MessageList,Node);
  600.         MessageListLen++;
  601.     }
  602.  
  603.     va_end(VarArgs);
  604.  
  605.     LT_SetAttributes(handle,
  606.         GTLV_Labels,MessageList,
  607.     TAG_DONE);
  608. }
  609.  
  610. STATIC VOID
  611. ResetUI(LayoutHandle *handle)
  612. {
  613.     STRPTR nothing;
  614.     LONG i;
  615.  
  616.     nothing = "-";
  617.  
  618.     for(i = 0 ; i < 4 ; i++)
  619.     {
  620.       LT_SetAttributes(handle,GAD_NameBox,
  621.           LABX_Index,        i,
  622.           LABX_Text,        nothing,
  623.       TAG_DONE);
  624.  
  625.       LT_SetAttributes(handle,GAD_BatchBox,
  626.           LABX_Index,        i,
  627.           LABX_Text,        nothing,
  628.       TAG_DONE);
  629.  
  630.       LT_SetAttributes(handle,GAD_TransferBox,
  631.           LABX_Index,        i,
  632.           LABX_Text,        nothing,
  633.       TAG_DONE);
  634.     }
  635. }
  636.  
  637. STATIC LayoutHandle *
  638. OpenUI (STRPTR Title)
  639. {
  640.     LayoutHandle *handle;
  641.  
  642.     if(!(MessageList = CreateList()))
  643.         return(NULL);
  644.  
  645.     MessageListLen = 0;
  646.  
  647.     if(handle = LT_CreateHandleTagList(Window->WScreen,NULL))
  648.     {
  649.         LT_New(handle,
  650.             LA_Type,    VERTICAL_KIND,
  651.         TAG_DONE);
  652.         {
  653.             LT_New(handle,
  654.                 LA_Type,VERTICAL_KIND,
  655.             TAG_DONE);
  656.             {
  657.                 STATIC STRPTR BoxLabels1[] =
  658.                 {
  659.                     "Name",
  660.                     "Size",
  661.                     "Completion",
  662.                     NULL
  663.                 };
  664.  
  665.                 STATIC STRPTR BoxLabels2[] =
  666.                 {
  667.                     "Batch count",
  668.                     "Batch size",
  669.                     "Batch completion",
  670.                     NULL
  671.                 };
  672.  
  673.                 STATIC STRPTR BoxLabels3[] =
  674.                 {
  675.                     "Number of errors",
  676.                     "Number of retries",
  677.                     "Bytes/second",
  678.                     NULL
  679.                 };
  680.  
  681.                 LT_New(handle,
  682.                     LA_Type,        BOX_KIND,
  683.                     LA_ID,            GAD_NameBox,
  684.                     LA_Chars,        40,
  685.                     LABX_Labels,    BoxLabels1,
  686.                 TAG_DONE);
  687.  
  688.                 LT_New(handle,
  689.                     LA_Type,        BOX_KIND,
  690.                     LA_ID,            GAD_BatchBox,
  691.                     LABX_Labels,    BoxLabels2,
  692.                 TAG_DONE);
  693.  
  694.                 LT_New(handle,
  695.                     LA_Type,        LISTVIEW_KIND,
  696.                     LA_ID,            GAD_MessageList,
  697.                     LA_NoKey,        TRUE,
  698.                     LA_LabelText,    "Information",
  699.                     LA_LabelPlace,    PLACE_Left,
  700.                     LALV_MaxGrowY,    5,
  701.                     LALV_Lines,        2,
  702.                     GTLV_ReadOnly,    TRUE,
  703.                 TAG_DONE);
  704.  
  705.                 LT_New(handle,
  706.                     LA_Type,        BOX_KIND,
  707.                     LA_ID,            GAD_TransferBox,
  708.                     LABX_Labels,    BoxLabels3,
  709.                 TAG_DONE);
  710.  
  711.                 LT_New(handle,
  712.                     LA_Type,        GAUGE_KIND,
  713.                     LA_ID,            GAD_FileProgress,
  714.                     LA_LabelText,    "File progress",
  715.                 TAG_DONE);
  716.  
  717.                 LT_New(handle,
  718.                     LA_Type,        GAUGE_KIND,
  719.                     LA_ID,            GAD_BatchProgress,
  720.                     LA_LabelText,    "Batch progress",
  721.                     GA_Disabled,    TRUE,
  722.                 TAG_DONE);
  723.  
  724.                 LT_EndGroup(handle);
  725.             }
  726.  
  727.             LT_New(handle,
  728.                 LA_Type,        VERTICAL_KIND,
  729.             TAG_DONE);
  730.             {
  731.                 LT_New(handle,
  732.                     LA_Type,        XBAR_KIND,
  733.                     LAXB_FullWidth,    TRUE,
  734.                 TAG_DONE);
  735.  
  736.                 LT_EndGroup(handle);
  737.             }
  738.  
  739.             LT_New(handle,
  740.                 LA_Type,        HORIZONTAL_KIND,
  741.                 LAGR_SameSize,    TRUE,
  742.                 LAGR_Spread,    TRUE,
  743.             TAG_DONE);
  744.             {
  745.  
  746.                 LT_New(handle,
  747.                     LA_Type,        BLANK_KIND,
  748.                 TAG_DONE);
  749.  
  750.                 LT_New(handle,
  751.                     LA_Type,        BUTTON_KIND,
  752.                     LA_ID,            GAD_Abort,
  753.                     LA_LabelText,    "Abort",
  754.                     LABT_ExtraFat,    TRUE,
  755.                     LABT_EscKey,    TRUE,
  756.                 TAG_DONE);
  757.  
  758.                 LT_EndGroup(handle);
  759.             }
  760.  
  761.             LT_EndGroup(handle);
  762.         }
  763.  
  764.         if(LT_Build(handle,
  765.             LAWN_TitleText,    Title,
  766.             LAWN_Zoom,        TRUE,
  767.             LAWN_Parent,    Window,
  768.             WA_DepthGadget,    TRUE,
  769.             WA_DragBar,        TRUE,
  770.             WA_RMBTrap,        TRUE,
  771.             WA_Activate,    TRUE,
  772.         TAG_DONE))
  773.         {
  774.             ResetUI(handle);
  775.             return(handle);
  776.         }
  777.  
  778.         LT_DeleteHandle(handle);
  779.     }
  780.  
  781.     DeleteList(MessageList);
  782.     MessageList = NULL;
  783.  
  784.     return(NULL);
  785. }
  786.  
  787. STATIC ULONG
  788. CalculateBPS()
  789. {
  790.     ULONG Now;
  791.  
  792.     Now = GetTime();
  793.  
  794.     if(Now > StartupTime)
  795.     {
  796.         ULONG Bytes;
  797.  
  798.         Bytes = Transmitting ? ZBytesOut : ZBytesIn;
  799.         Now -= StartupTime;
  800.  
  801.         LastBPS = Bytes / Now;
  802.     }
  803.  
  804.     return(LastBPS);
  805. }
  806.  
  807. STATIC VOID
  808. UpdateError (LONG error)
  809. {
  810.   if (error > 0)
  811.   {
  812.     UBYTE LocalBuffer[256];
  813.     STRPTR Message;
  814.  
  815.     switch (error)
  816.     {
  817.     case TIMEOUT:
  818.       Message = "Timeout";
  819.       break;
  820.     case CANCEL:
  821.       Message = "Local abort";
  822.       break;
  823.     case ABORT:
  824.       Message = "Remote abort";
  825.       break;
  826.     case ERROR:
  827.       Message = "Transmission error";
  828.       break;
  829.     case NO_CARRIER:
  830.       Message = "Carrier lost";
  831.       break;
  832.     case LINE_ERROR:
  833.       Message = "Hardware data overrun";
  834.       break;
  835.     case PARITY_ERROR:
  836.       Message = "Parity error";
  837.       break;
  838.     case TIMER_ERROR:
  839.       Message = "Timer error";
  840.       break;
  841.     case BUFFER_OVERFLOW:
  842.       Message = "Buffer overflow";
  843.       break;
  844.     case NO_DSR:
  845.       Message = "No DSR signal detected";
  846.       break;
  847.     case BREAK_DETECTED:
  848.       Message = "Break signal detected";
  849.       break;
  850.     default:
  851.       Fault (error, NULL, Message = LocalBuffer, sizeof (LocalBuffer));
  852.       break;
  853.     }
  854.  
  855.     AddMessage(Message);
  856.   }
  857. }
  858.  
  859. STATIC VOID
  860. NameProgress (STRPTR name)
  861. {
  862.     LT_SetAttributes(handle,GAD_NameBox,
  863.         LABX_Index,    NAMEBOX_Name,
  864.         LABX_Text,    name,
  865.     TAG_DONE);
  866. }
  867.  
  868. STATIC BOOL
  869. CheckCancel ()
  870. {
  871.   struct IntuiMessage *Message;
  872.   ULONG MsgClass;
  873.   UWORD MsgGadgetID;
  874.   BOOL Result;
  875.  
  876.   Result = FALSE;
  877.  
  878.   while (Message = LT_GetIMsg (handle))
  879.   {
  880.     MsgClass = Message->Class;
  881.  
  882.     if(MsgClass == IDCMP_GADGETUP)
  883.       MsgGadgetID = ((struct Gadget *)Message->IAddress)->GadgetID;
  884.  
  885.     LT_ReplyIMsg(Message);
  886.  
  887.     if(MsgClass == IDCMP_GADGETUP && MsgGadgetID == GAD_Abort)
  888.       Result = TRUE;
  889.   }
  890.  
  891.   return (Result);
  892. }
  893.  
  894. STATIC VOID
  895. UpdateRetries(LONG retries)
  896. {
  897.   UBYTE LocalBuffer[256];
  898.   STRPTR String;
  899.  
  900.   if(retries > 0)
  901.     LimitedSPrintf(sizeof(LocalBuffer),String = LocalBuffer,"%lD of %lD",retries,ZRetries);
  902.   else
  903.     String = "-";
  904.  
  905.   LT_SetAttributes(handle,GAD_TransferBox,
  906.       LABX_Index,        TRANSFERBOX_Retries,
  907.       LABX_Text,        String,
  908.   TAG_DONE);
  909. }
  910.  
  911. STATIC VOID
  912. UpdateBatch(ULONG number,ULONG numberLeft,ULONG bytes,ULONG bytesLeft)
  913. {
  914.   if(numberLeft > 0 && bytesLeft > 0)
  915.   {
  916.     UBYTE LocalBuffer[256];
  917.     ULONG Percent;
  918.     ULONG bps;
  919.  
  920.     LimitedSPrintf(sizeof(LocalBuffer),LocalBuffer,"%lD of %lD",number + 1,number + numberLeft);
  921.  
  922.     LT_SetAttributes(handle,GAD_BatchBox,
  923.         LABX_Index,        BATCHBOX_Count,
  924.         LABX_Text,        LocalBuffer,
  925.     TAG_DONE);
  926.  
  927.     LimitedSPrintf(sizeof(LocalBuffer),LocalBuffer,"%lD of %lD",bytes,bytes + bytesLeft);
  928.  
  929.     LT_SetAttributes(handle,GAD_BatchBox,
  930.         LABX_Index,        BATCHBOX_Size,
  931.         LABX_Text,        LocalBuffer,
  932.     TAG_DONE);
  933.  
  934.     if((bps = CalculateBPS()) > 0)
  935.     {
  936.       ULONG time;
  937.       STRPTR String;
  938.  
  939.       time = bytesLeft / bps;
  940.  
  941.       if(time < 1)
  942.         String = "< 00:00:01 h";
  943.       else
  944.         LimitedSPrintf(sizeof(LocalBuffer),String = LocalBuffer,"  %2ld:%02ld:%02ld h",time / (60 * 60),(time / 60) % 60,time % 60);
  945.  
  946.       LT_SetAttributes(handle,GAD_BatchBox,
  947.             LABX_Index,        BATCHBOX_Completion,
  948.             LABX_Text,        String,
  949.       TAG_DONE);
  950.  
  951.       LimitedSPrintf(sizeof(LocalBuffer),LocalBuffer,"%lD bytes/s",bps);
  952.  
  953.       LT_SetAttributes(handle,GAD_TransferBox,
  954.             LABX_Index,        TRANSFERBOX_CPS,
  955.             LABX_Text,        LocalBuffer,
  956.       TAG_DONE);
  957.     }
  958.  
  959.     Percent = (100 * number) / (number + numberLeft);
  960.  
  961.     if(Percent > 100)
  962.       Percent = 100;
  963.  
  964.     LT_SetAttributes(handle,GAD_BatchProgress,
  965.       GA_Disabled,  FALSE,
  966.       LAGA_Percent, Percent,
  967.     TAG_DONE);
  968.   }
  969.   else
  970.   {
  971.     LT_SetAttributes(handle,GAD_BatchProgress,
  972.       GA_Disabled,TRUE,
  973.     TAG_DONE);
  974.   }
  975. }
  976.  
  977. STATIC VOID
  978. UpdateProgress (LONG bytes, LONG maximum)
  979. {
  980.   UBYTE LocalBuffer[256];
  981.   ULONG Percent;
  982.  
  983.   if(bytes < maximum)
  984.   {
  985.     ULONG bps;
  986.  
  987.     if((bps = CalculateBPS()) > 0)
  988.     {
  989.       ULONG time,delta;
  990.       STRPTR String;
  991.  
  992.       delta = maximum - bytes;
  993.  
  994.       time = delta / bps;
  995.  
  996.       if(time < 1)
  997.         String = "< 00:00:01 h";
  998.       else
  999.         LimitedSPrintf(sizeof(LocalBuffer),String = LocalBuffer,"  %2ld:%02ld:%02ld h",time / (60 * 60),(time / 60) % 60,time % 60);
  1000.  
  1001.       LT_SetAttributes(handle,GAD_NameBox,
  1002.             LABX_Index,        NAMEBOX_Completion,
  1003.             LABX_Text,        String,
  1004.       TAG_DONE);
  1005.  
  1006.       LimitedSPrintf(sizeof(LocalBuffer),LocalBuffer,"%lD bytes/s",bps);
  1007.  
  1008.       LT_SetAttributes(handle,GAD_TransferBox,
  1009.             LABX_Index,        TRANSFERBOX_CPS,
  1010.             LABX_Text,        LocalBuffer,
  1011.       TAG_DONE);
  1012.     }
  1013.   }
  1014.  
  1015.   LimitedSPrintf(sizeof(LocalBuffer),LocalBuffer,"%lD of %lD",bytes,maximum);
  1016.  
  1017.   LT_SetAttributes(handle,GAD_NameBox,
  1018.       LABX_Index,        NAMEBOX_Size,
  1019.       LABX_Text,        LocalBuffer,
  1020.   TAG_DONE);
  1021.  
  1022.   if(maximum > 0)
  1023.     Percent = (100 * bytes) / maximum;
  1024.   else
  1025.     Percent = 0;
  1026.  
  1027.   LT_SetAttributes(handle,GAD_FileProgress,
  1028.       LAGA_Percent,    Percent,
  1029.   TAG_DONE);
  1030. }
  1031.  
  1032. /* ===== CRC =========================================================== */
  1033. /* crctab calculated by Mark G. Mendel, Network Systems Corporation */
  1034. STATIC UWORD crctab[256] =
  1035. {
  1036.   0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
  1037.   0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
  1038.   0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
  1039.   0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,
  1040.   0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,
  1041.   0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
  1042.   0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
  1043.   0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
  1044.   0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
  1045.   0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
  1046.   0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12,
  1047.   0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
  1048.   0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41,
  1049.   0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
  1050.   0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
  1051.   0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,
  1052.   0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
  1053.   0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
  1054.   0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,
  1055.   0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
  1056.   0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
  1057.   0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
  1058.   0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,
  1059.   0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
  1060.   0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,
  1061.   0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,
  1062.   0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
  1063.   0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,
  1064.   0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,
  1065.   0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
  1066.   0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,
  1067.   0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0
  1068. };
  1069.  
  1070. /*
  1071.  * updcrc macro derived from article Copyright (C) 1986 Stephen Satchell.
  1072.  *  NOTE: First argument must be in range 0 to 255.
  1073.  *        Second argument is referenced twice.
  1074.  *
  1075.  * Programmers may incorporate any or all code into their programs,
  1076.  * giving proper credit within the source. Publication of the
  1077.  * source routines is permitted so long as proper credit is given
  1078.  * to Stephen Satchell, Satchell Evaluations and Chuck Forsberg,
  1079.  * Omen Technology.
  1080.  */
  1081.  
  1082. #define updcrc(cp, crc) ( crctab[crc >> 8] ^ (((crc & 255) << 8) | (cp)))
  1083.  
  1084. /* ===== COMMON FUNCTIONS ============================================== */
  1085.  
  1086. STATIC VOID
  1087. ToOctal (ULONG v, UBYTE * s)
  1088. {
  1089.   UBYTE t[20], *u;
  1090.  
  1091.   u = t;
  1092.  
  1093.   do
  1094.   {
  1095.     *u++ = '0' + (v & 7);
  1096.  
  1097.     v >>= 3;
  1098.   }
  1099.   while (v != 0);
  1100.  
  1101.   while (u > t)
  1102.     *s++ = *(--u);
  1103.  
  1104.   *s = 0;
  1105. }
  1106.  
  1107. STATIC ULONG
  1108. GetTime ()
  1109. {
  1110.   struct timeval Now;
  1111.  
  1112.   GetSysTime (&Now);
  1113.  
  1114.   return (Now.tv_secs);
  1115. }
  1116.  
  1117. /* ----- Should we escape control characters? -------------------------- */
  1118.  
  1119. STATIC BOOL
  1120. Escape ()
  1121. {
  1122.   return ((BOOL) (ZEscapeCtl || Config->SerialConfig->BitsPerChar < 8));
  1123. }
  1124.  
  1125. /* ----- Allocate memory for transmit buffer --------------------------- */
  1126.  
  1127. STATIC LONG
  1128. NewTxBuffer (VOID)
  1129. {
  1130.   DB (kprintf ("»txbuffer size is %ld\«\n", block ? 2 * (ZMAXSPLEN + 6) : 32));
  1131.  
  1132.   TxBuffer = (UBYTE *) AllocVecPooled (3 * (ZMAXSPLEN + 6),MEMF_ANY);
  1133.  
  1134.   SerialBuffer = (UBYTE *) AllocVecPooled (SERIALBUFFERSIZE, MEMF_ANY);
  1135.  
  1136.   if (TxBuffer && SerialBuffer)
  1137.   {
  1138.     SerialIndex = SerialBuffer;
  1139.     SerialCache = 0;
  1140.  
  1141.     return (0);
  1142.   }
  1143.   else
  1144.     return (ERROR_NO_FREE_STORE);
  1145. }
  1146.  
  1147. /* ----- Dispose of transmit buffer ------------------------------------ */
  1148.  
  1149. STATIC VOID
  1150. DisposeTxBuffer ()
  1151. {
  1152.   FreeVecPooled (TxBuffer);
  1153.   TxBuffer = NULL;
  1154.  
  1155.   FreeVecPooled (SerialBuffer);
  1156.   SerialBuffer = NULL;
  1157. }
  1158.  
  1159. /* ----- Prepare new transmission -------------------------------------- */
  1160.  
  1161. STATIC VOID
  1162. InitTxBuffer ()
  1163. {
  1164.   TxPtr = TxBuffer;
  1165. }
  1166.  
  1167. /* ----- Start transmission, don't wait -------------------------------- */
  1168.  
  1169. STATIC VOID
  1170. TransmitTxBuffer ()
  1171. {
  1172.   if (TxPtr > TxBuffer)
  1173.   {
  1174.     ULONG Size;
  1175.  
  1176.     Size = (ULONG) TxPtr - (ULONG) TxBuffer;
  1177.  
  1178.     DoSerialWrite (TxBuffer, Size);
  1179.   }
  1180. }
  1181.  
  1182. /* ----- Send more bytes ----------------------------------------------- */
  1183.  
  1184. STATIC VOID
  1185. SendString (UBYTE * s, UWORD n)
  1186. {
  1187.   InitTxBuffer ();
  1188.   memcpy (TxPtr, s, n);
  1189.   TxPtr += n;
  1190.   TransmitTxBuffer ();
  1191. }
  1192.  
  1193. /* ----- Send cancel string -------------------------------------------- */
  1194.  
  1195. STATIC VOID
  1196. SendCancel ()
  1197. {
  1198.   STATIC UBYTE cs[] =
  1199.   {
  1200.     CAN, CAN, CAN, CAN, CAN, CAN, CAN, CAN,
  1201.     BS, BS, BS, BS, BS, BS, BS, BS, BS, BS
  1202.   };
  1203.  
  1204.   DoSerialWrite (cs, sizeof (cs));
  1205. }
  1206.  
  1207. /* ----- Send a byte --------------------------------------------------- */
  1208.  
  1209. STATIC UBYTE
  1210. SendByte (UBYTE c)
  1211. {
  1212.   *TxPtr++ = c;
  1213.   return c;
  1214. }
  1215.  
  1216. /* ----- Send attention string ----------------------------------------- */
  1217.  
  1218. STATIC VOID
  1219. SendAttn (UBYTE * s)
  1220. {
  1221.   while (*s)
  1222.   {
  1223.     switch (*s)
  1224.     {
  1225.     case 0xDD:                  /* Send break signal */
  1226.  
  1227.       DoSerialBreak ();
  1228.       break;
  1229.  
  1230.     case 0xDE:                  /* Sleep 1 second */
  1231.  
  1232.       DelayTime (1, 0);
  1233.       break;
  1234.  
  1235.     default:
  1236.  
  1237.       DoSerialWrite (s, 1);
  1238.       break;
  1239.     }
  1240.  
  1241.     ++s;
  1242.   }
  1243. }
  1244.  
  1245. /* ----- Swap 2 bytes in WORD ----------------------------------------- */
  1246.  
  1247. STATIC UWORD __inline
  1248. Swap2 (UWORD n)
  1249. {
  1250.   return ((UWORD) (((n & 0xFF) << 8) | (n >> 8)));
  1251. }
  1252.  
  1253. /* ----- Swap 4 bytes in long ------------------------------------------ */
  1254.  
  1255. STATIC ULONG __inline
  1256. Swap4 (ULONG n)
  1257. {
  1258.   return (((ULONG) Swap2 (n & 0xffff) << 16) | Swap2 (n >> 16));
  1259. }
  1260.  
  1261. /* ----- Adjust header ------------------------------------------------- */
  1262.  
  1263. STATIC VOID
  1264. AdjustHeader (UBYTE type, HEADER * p)
  1265. {
  1266.   switch (type)
  1267.   {
  1268.   case ZACK:
  1269.   case ZRPOS:
  1270.   case ZDATA:
  1271.   case ZEOF:
  1272.   case ZCRC:
  1273.   case ZCHALLENGE:
  1274.   case ZFREECNT:
  1275.   case ZCOMPL:
  1276.  
  1277.     p->position = Swap4 (p->position);
  1278.     break;
  1279.  
  1280.   case ZRINIT:
  1281.  
  1282.     p->zrinit.bufsize = Swap2 (p->zrinit.bufsize);
  1283.     break;
  1284.   }
  1285. }
  1286.  
  1287. /* ----- Send a byte as two hex digits --------------------------------- */
  1288.  
  1289. STATIC UBYTE
  1290. PutHex (UBYTE c)
  1291. {
  1292.   STATIC UBYTE digits[] =
  1293.   {
  1294.     '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
  1295.   };
  1296.  
  1297.   SendByte (digits[(c >> 4) & 0xF]);
  1298.   SendByte (digits[c & 0xF]);
  1299.  
  1300.   return c;
  1301. }
  1302.  
  1303. /* ----- Send a byte with ZMODEM escape sequence encoding -------------- */
  1304.  
  1305. STATIC UBYTE
  1306. LocalSendLine (UBYTE c)
  1307. {
  1308.   UBYTE b = c;
  1309.  
  1310.   STATIC UBYTE lastsent = 0;    /* Last byte sent */
  1311.  
  1312.   if (c & 0x60)                 /* Non control character */
  1313.     SendByte (lastsent = c);
  1314.   else
  1315.   {
  1316.     switch (c)
  1317.     {
  1318.     case ZDLE:
  1319.  
  1320.       SendByte (ZDLE);
  1321.       SendByte (lastsent = c ^ 0x40);
  1322.       break;
  1323.  
  1324.     case CR:                    /* Escape CR followed by @ (Telenet net escape) */
  1325.     case 0x80 | CR:
  1326.  
  1327.       if (!Zctlesc && (lastsent & 0x7F) != '@')
  1328.       {
  1329.         SendByte (lastsent = c);
  1330.         break;
  1331.       }
  1332.  
  1333.       /* Fall thru */
  1334.  
  1335.     case DLE:
  1336.     case 0x80 | DLE:
  1337.     case XON:
  1338.     case 0x80 | XON:
  1339.     case XOFF:
  1340.     case 0x80 | XOFF:
  1341.  
  1342.       SendByte (ZDLE);
  1343.       c ^= 0x40;
  1344.       SendByte (lastsent = c);
  1345.       break;
  1346.  
  1347.     default:
  1348.  
  1349.       if (Zctlesc && !(c & 0x60))
  1350.       {
  1351.         SendByte (ZDLE);
  1352.         c ^= 0x40;
  1353.       }
  1354.  
  1355.       SendByte (lastsent = c);
  1356.     }
  1357.   }
  1358.   return b;
  1359. }
  1360.  
  1361. /* ----- Send ZMODEM HEX header ---------------------------------------- */
  1362.  
  1363. STATIC VOID
  1364. SendHexHeader (UBYTE type, HEADER * hdr)
  1365. {
  1366.   WORD i;
  1367.   UWORD crc;
  1368.  
  1369.   AdjustHeader (type, hdr);
  1370.  
  1371.   InitTxBuffer ();
  1372.   SendByte (ZPAD);              /* 1 */
  1373.   SendByte (ZPAD);              /* 1 */
  1374.   SendByte (ZDLE);              /* 1 */
  1375.   SendByte (ZHEX);              /* 1 */
  1376.  
  1377.   crc = updcrc (PutHex (type), 0);
  1378.  
  1379.   for (i = 0; i < 4; ++i)
  1380.     crc = updcrc (PutHex (hdr->b[i]), crc);     /* 8 */
  1381.  
  1382.   crc = updcrc (0, updcrc (0, crc));
  1383.  
  1384.   PutHex (crc >> 8);            /* 2 */
  1385.   PutHex (crc & 0xFF);          /* 2 */
  1386.  
  1387.   SendByte (CR);                /* 1 */
  1388.   SendByte (LF);                /* 1 */
  1389.  
  1390.   if (type != ZFIN && type != ZACK)
  1391.     SendByte (XON);             /* 1 */
  1392.  
  1393.   TransmitTxBuffer ();          /* Worst case:  19 bytes */
  1394. }
  1395.  
  1396. /* ----- Send ZMODEM binary header ------------------------------------- */
  1397.  
  1398. STATIC VOID
  1399. SendBinaryHeader (UBYTE type, HEADER * hdr)
  1400. {
  1401.   WORD i;
  1402.   UWORD crc;
  1403.  
  1404.   AdjustHeader (type, hdr);
  1405.  
  1406.   InitTxBuffer ();
  1407.   SendByte (ZPAD);              /* 1 */
  1408.   SendByte (ZDLE);              /* 1 */
  1409.   SendByte (ZBIN);              /* 1 */
  1410.  
  1411.   crc = updcrc (LocalSendLine (type), 0);
  1412.  
  1413.   for (i = 0; i < 4; ++i)
  1414.     crc = updcrc (LocalSendLine (hdr->b[i]), crc);      /* 8 */
  1415.  
  1416.   crc = updcrc (0, updcrc (0, crc));
  1417.   LocalSendLine (crc >> 8);     /* 2 */
  1418.   LocalSendLine (crc & 0xFF);   /* 2 */
  1419.  
  1420.   TransmitTxBuffer ();          /* Worst case:  15 bytes */
  1421. }
  1422.  
  1423. /* ----- Send binary array with ending ZDLE sequence ------------------- */
  1424.  
  1425. STATIC VOID
  1426. SendData (UBYTE * buf, UWORD length, UBYTE frameend)
  1427. {
  1428.   UWORD crc;
  1429.  
  1430.   InitTxBuffer ();
  1431.   for (crc = 0; length; ++buf, --length)
  1432.   {
  1433.     LocalSendLine (*buf);
  1434.     crc = updcrc (*buf, crc);
  1435.   }
  1436.   SendByte (ZDLE);
  1437.   SendByte (frameend);
  1438.   crc = updcrc (frameend, crc);
  1439.   crc = updcrc (0, updcrc (0, crc));
  1440.   LocalSendLine (crc >> 8);
  1441.   LocalSendLine (crc & 0xFF);
  1442.  
  1443.   if (frameend == ZCRCW)
  1444.     SendByte (XON);
  1445.  
  1446.   TransmitTxBuffer ();
  1447. }
  1448.  
  1449. /* ----- Read character with timeout ----------------------------------- */
  1450.  
  1451. STATIC WORD
  1452. ReadByte (UBYTE * c, ULONG timeout)
  1453. {
  1454.   STATIC WORD abort;            /* CAN counter */
  1455.  
  1456.   LONG err;
  1457.  
  1458.   DB (kprintf ("» ReadByte timeout=%ld\n", timeout));
  1459.  
  1460.   if (SerialCache > 0)
  1461.   {
  1462.     if (SetSignal (0, (1L << handle->Window->UserPort->mp_SigBit)) & (1L << handle->Window->UserPort->mp_SigBit))
  1463.     {
  1464.       if (CheckCancel ())
  1465.         longjmp (CancelEnv, CANCEL);
  1466.     }
  1467.  
  1468.     *c = *SerialIndex++;
  1469.  
  1470.     if (--SerialCache == 0)
  1471.     {
  1472.       DB (kprintf ("» Cache exhausted\n"));
  1473.  
  1474.       SerialIndex = SerialBuffer;
  1475.  
  1476.       if (CheckSerialRead ())
  1477.       {
  1478.         err = WaitSerialRead ();
  1479.  
  1480.         if (err)
  1481.         {
  1482.           if (err = MapSerialError (err))
  1483.             longjmp (CancelEnv, err);
  1484.         }
  1485.  
  1486.         SerialBuffer[0] = zc;
  1487.  
  1488.         if (SerialCache = GetSerialWaiting ())
  1489.         {
  1490.           if (SerialCache > SERIALBUFFERSIZE - 1)
  1491.             SerialCache = SERIALBUFFERSIZE - 1;
  1492.  
  1493.           err = DoSerialRead (&SerialBuffer[1], SerialCache);
  1494.  
  1495.           if (err)
  1496.           {
  1497.             if (err = MapSerialError (err))
  1498.               longjmp (CancelEnv, err);
  1499.           }
  1500.         }
  1501.  
  1502.         SerialCache++;
  1503.  
  1504.         StartSerialRead (&zc, 1);
  1505.       }
  1506.  
  1507.       DB (kprintf ("» Cache size = %ld\n", SerialCache));
  1508.     }
  1509.   }
  1510.   else
  1511.   {
  1512.     ULONG mask;
  1513.  
  1514.     if (!timeout)
  1515.       return (NOTHING);
  1516.  
  1517.     StartTime (timeout,0);
  1518.  
  1519.     for (;;)
  1520.     {
  1521.       mask = Wait (SIG_TIMER | SIG_SERIAL | (1L << handle->Window->UserPort->mp_SigBit));
  1522.  
  1523.       if (mask & (1L << handle->Window->UserPort->mp_SigBit))
  1524.       {
  1525.         if (CheckCancel ())
  1526.         {
  1527.           StopTime ();
  1528.  
  1529.           longjmp (CancelEnv, CANCEL);
  1530.         }
  1531.       }
  1532.  
  1533.       if (mask & SIG_SERIAL)
  1534.       {
  1535.         err = WaitSerialRead ();
  1536.  
  1537.         StopTime ();
  1538.  
  1539.         if (err)
  1540.         {
  1541.           if (err = MapSerialError (err))
  1542.             longjmp (CancelEnv, err);
  1543.         }
  1544.  
  1545.         *c = zc;
  1546.  
  1547.         if (SerialCache = GetSerialWaiting ())
  1548.         {
  1549.           if (SerialCache > SERIALBUFFERSIZE)
  1550.             SerialCache = SERIALBUFFERSIZE;
  1551.  
  1552.           err = DoSerialRead (SerialBuffer, SerialCache);
  1553.  
  1554.           if (err)
  1555.           {
  1556.             if (err = MapSerialError (err))
  1557.               longjmp (CancelEnv, err);
  1558.           }
  1559.         }
  1560.  
  1561.         StartSerialRead (&zc, 1);
  1562.  
  1563.         DB (kprintf ("» Cache size = %ld\n", SerialCache));
  1564.  
  1565.         break;
  1566.       }
  1567.  
  1568.       if (mask & SIG_TIMER)
  1569.       {
  1570.         WaitTime ();
  1571.  
  1572.         if (GetSerialStatus () & CIAF_COMCD)
  1573.           longjmp (CancelEnv, NO_CARRIER);
  1574.         else
  1575.           return (TIMEOUT);
  1576.       }
  1577.     }
  1578.   }
  1579.  
  1580.   if (*c == CAN)
  1581.   {
  1582.     if (++abort >= 5)
  1583.       longjmp (CancelEnv, ABORT);
  1584.   }
  1585.   else
  1586.     abort = 0;
  1587.  
  1588.   return FINE;
  1589. }
  1590.  
  1591. /* ----- Wait for ZPAD character --------------------------------------- */
  1592.  
  1593. STATIC WORD
  1594. WaitZPAD (
  1595.            ULONG timeout,       /* Timeout in ticks */
  1596.            UWORD n)             /* Max bytes before start of frame */
  1597. {
  1598.   UBYTE c;
  1599.  
  1600.   for (;;)
  1601.   {
  1602.     switch (ReadByte (&c, timeout))
  1603.     {
  1604.     case FINE:
  1605.  
  1606.       if ((c & 0x7F) == ZPAD)
  1607.         return FINE;
  1608.  
  1609.       if (--n == 0)
  1610.         return ERROR;
  1611.  
  1612.       break;
  1613.  
  1614.     case TIMEOUT:
  1615.  
  1616.       return TIMEOUT;
  1617.     }
  1618.   }
  1619. }
  1620.  
  1621. /* ----- Read character with timemout, eat parity, XON and XOFF -------- */
  1622.  
  1623. STATIC WORD
  1624. ReadByte2 (
  1625.             UBYTE * c,          /* Character (result) */
  1626.             ULONG timeout)      /* Timeout in ticks */
  1627. {
  1628.   for (;;)
  1629.   {
  1630.     if (ReadByte (c, timeout))
  1631.       return TIMEOUT;
  1632.  
  1633.     switch (*c &= 0x7F)
  1634.     {
  1635.     case XON:
  1636.     case XOFF:
  1637.  
  1638.       break;
  1639.  
  1640.     default:
  1641.  
  1642.       if (Zctlesc && !(*c & 0x60))
  1643.         break;
  1644.  
  1645.     case CR:
  1646.     case LF:
  1647.     case ZDLE:
  1648.  
  1649.       return FINE;
  1650.     }
  1651.   }
  1652. }
  1653.  
  1654. /* ----- Decode two lower case hex digits into an 8 bit byte value ----- */
  1655.  
  1656. STATIC WORD
  1657. GetHex (UBYTE * c)
  1658. {
  1659.   WORD i;
  1660.   UBYTE n;
  1661.  
  1662.   *c = 0;
  1663.  
  1664.   for (i = 0; i < 2; ++i)
  1665.   {
  1666.     *c <<= 4;
  1667.  
  1668.     if (ReadByte2 (&n, ZTimeout))
  1669.       return TIMEOUT;
  1670.  
  1671.     if (n >= '0' && n <= '9')
  1672.       n -= '0';
  1673.     else
  1674.     {
  1675.       if (n >= 'a' && n <= 'f')
  1676.         n -= 'a' - 10;
  1677.       else
  1678.         return ERROR;
  1679.     }
  1680.  
  1681.     *c += n;
  1682.   }
  1683.  
  1684.   return FINE;
  1685. }
  1686.  
  1687. /* ----- Read a byte, checking for ZMODEM escape encoding -------------- */
  1688.  
  1689. STATIC WORD
  1690. ZdleRead (UBYTE * c)
  1691. {
  1692.   do
  1693.   {
  1694.     if (ReadByte (c, ZTimeout))
  1695.       return TIMEOUT;
  1696.  
  1697.     if (*c & 0x60)              /* Non-control character */
  1698.       return FINE;
  1699.  
  1700.     switch (*c)                 /* Control character */
  1701.     {
  1702.     case ZDLE:
  1703.     case XON:
  1704.     case 0x80 | XON:
  1705.     case XOFF:
  1706.     case 0x80 | XOFF:
  1707.  
  1708.       break;                    /* Ignore XON/XOFF */
  1709.  
  1710.     default:
  1711.  
  1712.       if (!Zctlesc)
  1713.         return FINE;
  1714.  
  1715.       break;                    /* Ignore ctrl if escaping */
  1716.     }
  1717.   }
  1718.   while (*c != ZDLE);
  1719.  
  1720.   /* Previous character was ZDLE */
  1721.  
  1722.   for (;;)
  1723.   {
  1724.     if (ReadByte (c, ZTimeout))
  1725.       return TIMEOUT;
  1726.  
  1727.     switch (*c)
  1728.     {
  1729.     case ZCRCE:
  1730.     case ZCRCG:
  1731.     case ZCRCQ:
  1732.     case ZCRCW:
  1733.  
  1734.       return (WORD) (*c | GOTOR);       /* Frame end */
  1735.  
  1736.     case ZRUB0:
  1737.  
  1738.       *c = DEL;
  1739.       return FINE;
  1740.  
  1741.     case ZRUB1:
  1742.  
  1743.       *c = 0x80 | DEL;
  1744.       return FINE;
  1745.  
  1746.     case XON:
  1747.     case 0x80 | XON:
  1748.     case XOFF:
  1749.     case 0x80 | XOFF:
  1750.  
  1751.       break;                    /* Ignore XON/XOFF */
  1752.  
  1753.     default:
  1754.       if (Zctlesc && !(*c & 0x60))
  1755.         break;                  /* Ignore ctrl if escaping */
  1756.  
  1757.       if ((*c & 0x60) != 0x40)  /* Must be -10- ---- */
  1758.         return ERROR;
  1759.  
  1760.       *c ^= 0x40;               /* Invert bit 6 */
  1761.  
  1762.       return FINE;
  1763.     }
  1764.   }
  1765. }
  1766.  
  1767. /* ----- Receive a binary style header (type and position) ------------- */
  1768.  
  1769. STATIC WORD
  1770. ReceiveBinaryHeader (UBYTE * type,UBYTE * hdr)
  1771. {
  1772.   WORD n, err;
  1773.   UWORD crc;
  1774.   UBYTE c;
  1775.  
  1776.   /* Header type */
  1777.  
  1778.   if (err = ZdleRead (type))
  1779.     return err;                 /* TIMEOUT, ERROR, GOTxxxx */
  1780.   crc = updcrc (*type, 0);
  1781.  
  1782.   /* header info (4 bytes) */
  1783.   for (n = 0; n < 4; ++n, ++hdr)
  1784.   {
  1785.     if (err = ZdleRead (hdr))
  1786.       return err;               /* TIMEOUT, ERROR, GOTxxxx */
  1787.     crc = updcrc (*hdr, crc);
  1788.   }
  1789.  
  1790.   /* CRC (2 bytes) */
  1791.   if (err = ZdleRead (&c))
  1792.     return err;                 /* TIMEOUT, ERROR, GOTxxxx */
  1793.   crc = updcrc (c, crc);
  1794.   if (err = ZdleRead (&c))
  1795.     return err;                 /* TIMEOUT, ERROR, GOTxxxx */
  1796.   if (crc = updcrc (c, crc))
  1797.     return ERROR;               /* Bad CRC */
  1798.  
  1799.   return FINE;
  1800. }
  1801.  
  1802. /* ----- Receive a hex style header (type and position) ---------------- */
  1803.  
  1804. STATIC WORD
  1805. ReceiveHexHeader (UBYTE * type,UBYTE * hdr)
  1806. {
  1807.   WORD n, err;
  1808.   UWORD crc;
  1809.   UBYTE c;
  1810.  
  1811.   DB (kprintf ("»ReceiveHexHeader«\n"));
  1812.  
  1813.   /* Header type */
  1814.  
  1815.   if (err = GetHex (type))
  1816.     return err;
  1817.   DB (kprintf ("»header type is %ld«\n", *type));
  1818.   crc = updcrc (*type, 0);
  1819.  
  1820.   /* Header info (4 bytes) */
  1821.  
  1822.   for (n = 0; n < 4; ++n, ++hdr)
  1823.   {
  1824.     if (err = GetHex (hdr))
  1825.       return err;
  1826.     crc = updcrc (*hdr, crc);
  1827.   }
  1828.  
  1829.   DB (kprintf ("»header info is %08lx, crc %02lx«\n", *(ULONG *) & hdr[-4], crc));
  1830.  
  1831.   /* CRC (2 bytes) */
  1832.  
  1833.   if (err = GetHex (&c))
  1834.     return err;
  1835.   DB (kprintf ("»%lx", c));
  1836.   crc = updcrc (c, crc);
  1837.   if (err = GetHex (&c))
  1838.     return err;
  1839.   DB (kprintf ("%lx«\n", c));
  1840.   if (crc = updcrc (c, crc))
  1841.   {
  1842.     DB (kprintf ("»CRC %02lx is wrong«\n", crc));
  1843.     return ERROR;               /* Bad CRC */
  1844.   }
  1845.  
  1846.   /* Throw away possible CR/LF */
  1847.  
  1848.   if (ReadByte (&c, 1) == FINE && (c & 0x7F) == CR)
  1849.     ReadByte (&c, 1);
  1850.  
  1851.   return FINE;
  1852. }
  1853.  
  1854. /* ----- Read a ZMODEM header (either binary or hex) ------------------- */
  1855.  
  1856. STATIC WORD
  1857. GetHeader (UBYTE * type,HEADER * hdr)
  1858. {
  1859.   WORD err;
  1860.   WORD n = GARBAGE;
  1861.   UBYTE c;
  1862.  
  1863.   DB (kprintf ("»GetHeader«\n"));
  1864.  
  1865.   /* Wait for ZPAD character */
  1866.  
  1867.   if (err = WaitZPAD (ZTimeout, GARBAGE))
  1868.   {
  1869.     DB (kprintf ("»didn't get ZPAD«\n"));
  1870.     goto done;
  1871.   }
  1872.  
  1873.   /* Just got ZPAD character, wait for ZDLE character */
  1874.  
  1875.   do
  1876.   {
  1877.     if (err = ReadByte2 (&c, ZTimeout))
  1878.     {
  1879.       DB (kprintf ("»didn't get ZDLE«\n"));
  1880.       goto done;
  1881.     }
  1882.     switch (c)
  1883.     {
  1884.     case ZDLE:                  /* This is what we want */
  1885.     case ZPAD:                  /* May be more than one ZPAD */
  1886.     case ZPAD | 0x80:
  1887.       break;
  1888.     again:
  1889.     default:                    /* Garbage */
  1890.       if (--n == 0)
  1891.       {
  1892.         err = ERROR;
  1893.         goto done;
  1894.       }
  1895.       break;
  1896.     }
  1897.   }
  1898.   while (c != ZDLE);
  1899.  
  1900.   /* Just got ZPAD-ZDLE sequence */
  1901.  
  1902.   if (err = ReadByte2 (&c, ZTimeout))
  1903.   {
  1904.     DB (kprintf ("»didn't get header type«\n"));
  1905.     goto done;
  1906.   }
  1907.   DB (kprintf ("»got header type %ld«\n", c));
  1908.   switch (c)
  1909.   {
  1910.   case ZBIN:                    /* It's a binary header */
  1911.     err = ReceiveBinaryHeader (type, hdr->b);
  1912.     break;
  1913.   case ZHEX:                    /* It's a hex header */
  1914.     err = ReceiveHexHeader (type, hdr->b);
  1915.     break;
  1916.   default:                      /* Garbage */
  1917.     goto again;
  1918.   }
  1919.   AdjustHeader (*type, hdr);
  1920.  
  1921.   /* ZPAD ZDLE ZBIN/ZHEX ... */
  1922.  
  1923. done:
  1924.   DB (kprintf ("»GetHeader done, err %ld«\n", err));
  1925.   return err;
  1926. }
  1927.  
  1928. /* ----- Receive array with ending ZDLE sequence and CRC --------------- */
  1929.  
  1930. STATIC WORD
  1931. ReceiveData (
  1932.               UBYTE * buf,      /* Buffer for data */
  1933.               UWORD length,     /* Max size of buffer */
  1934.               UWORD * count,    /* Number of data bytes received */
  1935.               UBYTE * framend)  /* Frame end character received */
  1936. {
  1937.   UWORD crc = 0;
  1938.   UBYTE *max = buf + length;
  1939.   WORD err;
  1940.   UBYTE c;
  1941.  
  1942.   *framend = 0xFF;
  1943.   while (buf <= max)
  1944.   {
  1945.     switch (err = ZdleRead (&c))
  1946.     {
  1947.     case FINE:                  /* Data byte */
  1948.       crc = updcrc (c, crc);
  1949.  
  1950.       if (ConvertNL)
  1951.       {
  1952.         if (c != CR)
  1953.           *buf++ = c;
  1954.       }
  1955.       else
  1956.         *buf++ = c;
  1957.  
  1958.       break;
  1959.     case GOTCRCE:               /* Frame end, CRC follows */
  1960.     case GOTCRCG:
  1961.     case GOTCRCQ:
  1962.     case GOTCRCW:
  1963.       *count = length - (max - buf);
  1964.       crc = updcrc (*framend = c, crc);
  1965.       if (err = ZdleRead (&c))
  1966.         goto done;
  1967.       crc = updcrc (c, crc);
  1968.       if (err = ZdleRead (&c))
  1969.         goto done;
  1970.       if (crc = updcrc (c, crc))
  1971.         err = ERROR;            /* Bad CRC */
  1972.       else
  1973.         err = FINE;
  1974.       goto done;
  1975.     default:                    /* TIMEOUT, ERROR, ... */
  1976.       *count = length - (max - buf);
  1977.       goto done;
  1978.     }
  1979.   }
  1980.   *count = length - (max - buf);
  1981.   err = ERROR;                  /* Data subpacket too LONG */
  1982.  
  1983. done:
  1984.   return err;
  1985. }
  1986.  
  1987. /* ----- Convert between Amiga time and ZModem time ---------------- */
  1988.  
  1989. STATIC VOID
  1990. ConvertTime (BOOL toAmiga, ULONG * t)
  1991. {
  1992.   const ULONG UTC_Offset = 252482400;
  1993.  
  1994.   if (toAmiga)
  1995.   {
  1996.     if (*t < UTC_Offset)
  1997.       *t = 0;
  1998.     else
  1999.       *t = *t - UTC_Offset;
  2000.   }
  2001.   else
  2002.     *t = *t + UTC_Offset;
  2003. }
  2004.  
  2005. /* ===== TRANSMIT ====================================================== */
  2006.  
  2007. /* ----- Fill buffer --------------------------------------------------- */
  2008.  
  2009. STATIC LONG
  2010. LocalReadBuffer (LONG * count,UBYTE * buf)
  2011. {
  2012.   *count = Read (FileHandle, buf, *count);
  2013.  
  2014.   return (*count == 0);
  2015. }
  2016.  
  2017. STATIC LONG
  2018. LocalReadBufferAndConvert (LONG * count,UBYTE * buf,UBYTE * lastc)
  2019. {
  2020.   LONG last, c;
  2021.   LONG i;
  2022.  
  2023.   last = *lastc;
  2024.  
  2025.   for (i = 0; i < *count; i++)
  2026.   {
  2027.     if ((c = FGetC (FileHandle)) < 0)
  2028.     {
  2029.       *count = i;
  2030.       *lastc = last;
  2031.       return (TRUE);
  2032.     }
  2033.  
  2034.     if (c == LF && last != CR)
  2035.     {
  2036.       UnGetC (FileHandle, LF);
  2037.       c = CR;
  2038.     }
  2039.  
  2040.     *buf++ = c;
  2041.     last = c;
  2042.   }
  2043.  
  2044.   *lastc = last;
  2045.  
  2046.   return (FALSE);
  2047. }
  2048.  
  2049. /* ----- Set file position --------------------------------------------- */
  2050.  
  2051. STATIC VOID
  2052. SetFilePosition (ULONG position)
  2053. {
  2054.   Seek (FileHandle, position, OFFSET_BEGINNING);
  2055. }
  2056.  
  2057. /* ----- Respond to receiver's complaint, get back in sync ------------- */
  2058.  
  2059. STATIC WORD
  2060. GetInSync (
  2061.             BOOL flag,          /* TRUE: ZACK always returns */
  2062.             UBYTE * type)
  2063. {
  2064.   WORD retry;
  2065.   WORD err;
  2066.   HEADER hdr;
  2067.  
  2068.   for (retry = 0; retry <= ZRetries; ++retry)
  2069.   {
  2070.     AddMessage("Synchronizing...");
  2071.     UpdateRetries(retry);
  2072.  
  2073.     switch (GetHeader (type, &hdr))
  2074.     {
  2075.     case FINE:
  2076.       switch (*type)
  2077.       {
  2078.       case ZRPOS:
  2079.         SetFilePosition (hdr.position);
  2080.         Lrxpos = Mark = hdr.position;
  2081.         if (Lastsync == hdr.position &&
  2082.             ++BeenHereB4 > 4 && Count > 32)
  2083.           Count /= 2;
  2084.         Lastsync = hdr.position;
  2085.         err = FINE;
  2086.         goto done;
  2087.       case ZACK:
  2088.         Lrxpos = hdr.position;
  2089.         if (flag || Mark == hdr.position)
  2090.         {
  2091.           err = FINE;
  2092.           goto done;
  2093.         }
  2094.         break;                  /* Ignore this ZACK */
  2095.       case ZCAN:
  2096.       case ZABORT:
  2097.       case ZFIN:
  2098.         goto error;
  2099.       case ZRINIT:
  2100.       case ZSKIP:
  2101.         err = FINE;
  2102.         goto done;
  2103.       }
  2104.       break;
  2105.     case TIMEOUT:
  2106.       goto error;
  2107.     }
  2108.     hdr.position = 0;
  2109.     SendHexHeader (ZNAK, &hdr);
  2110.   }
  2111. error:
  2112.   err = ERROR;
  2113. done:
  2114.   return err;
  2115. }
  2116.  
  2117. /* ----- Send the data in the file ------------------------------------- */
  2118.  
  2119. STATIC WORD
  2120. SendFileData ()
  2121. {
  2122.   WORD junkcount;               /* Counts garbage chars received */
  2123.   ULONG txwcnt;                 /* Counter used to space ACK requests */
  2124.   WORD eofseen;                 /* End of file seen (file read error) */
  2125.   UBYTE type;                   /* Header type */
  2126.   HEADER hdr;                   /* Header */
  2127.   WORD err;                     /* Error code */
  2128.   WORD newcnt;                  /* Controls receiver's buffer */
  2129.   LONG n;                       /* Transmit buffer length */
  2130.   UBYTE e;                      /* Frame end character */
  2131.   UBYTE c;
  2132.   UBYTE lastc;
  2133.  
  2134.   Lastsync = (StartPos = Lrxpos = Mark) - 1;
  2135.   junkcount = 0;
  2136.   BeenHereB4 = 0;
  2137.   lastc = 0;
  2138.  
  2139. somemore:
  2140.   if (FALSE)
  2141.   {
  2142.  
  2143.   waitack:
  2144.     junkcount = 0;
  2145.     err = GetInSync (FALSE, &type);
  2146.   gotack:
  2147.     if (err)
  2148.       return ERROR;
  2149.     switch (type)
  2150.     {
  2151.     case ZSKIP:
  2152.     case ZRINIT:
  2153.       return FINE;
  2154.     case ZACK:
  2155.     case ZRPOS:
  2156.       break;
  2157.     default:
  2158.       return ERROR;
  2159.     }
  2160.  
  2161.     /* Check reverse channel (but don't wait) */
  2162.  
  2163.     if (CheckCancel ())
  2164.       longjmp (CancelEnv, CANCEL);
  2165.     while (ReadByte (&c, 0) == FINE)
  2166.     {
  2167.       switch (c)
  2168.       {
  2169.       case CAN:
  2170.       case ZPAD:
  2171.         err = GetInSync (TRUE, &type);
  2172.         goto gotack;
  2173.       case XOFF:                /* Wait a while for an XON */
  2174.       case XOFF | 0x80:
  2175.         ReadByte (&c, 100);
  2176.       }
  2177.     }
  2178.  
  2179.   }                             /* somemore */
  2180.  
  2181.   /* Send ZDATA header */
  2182.  
  2183.   hdr.position = Mark;
  2184.   SendBinaryHeader (ZDATA, &hdr);
  2185.  
  2186.   /* Send one or more data subpackets */
  2187.  
  2188.   newcnt = Rxbuflen;
  2189.   txwcnt = 0;
  2190.   do
  2191.   {
  2192.     UpdateProgress (Mark,Length);
  2193.  
  2194.     /* Send data subpacket */
  2195.  
  2196.     n = Count;
  2197.  
  2198.     if (ConvertNL)
  2199.       eofseen = LocalReadBufferAndConvert (&n, Buffer, &lastc);
  2200.     else
  2201.       eofseen = LocalReadBuffer (&n, Buffer);
  2202.  
  2203.     if (eofseen)
  2204.       e = ZCRCE;
  2205.     else if (junkcount > 3)
  2206.       e = ZCRCW;
  2207.     else if (Mark == Lastsync)
  2208.       e = ZCRCW;
  2209.     else if (Rxbuflen && (newcnt -= n) <= 0)
  2210.       e = ZCRCW;
  2211.     else if (Txwindow && (txwcnt += n) >= Txwspac)
  2212.     {
  2213.       txwcnt = 0;
  2214.       e = ZCRCQ;
  2215.     }
  2216.     else
  2217.       e = ZCRCG;
  2218.  
  2219.     SendData (Buffer, n, e);
  2220.  
  2221.     ZBytesOut += n;
  2222.     Mark += n;
  2223.  
  2224.     /* Wait for ZACK if necessary */
  2225.  
  2226.     if (e == ZCRCW)
  2227.       goto waitack;
  2228.  
  2229.     /* Check reverse channel (but don't wait) */
  2230.  
  2231.     if (CheckCancel ())
  2232.       longjmp (CancelEnv, CANCEL);
  2233.     while (ReadByte (&c, 0) == FINE)
  2234.     {
  2235.       switch (c)
  2236.       {
  2237.       case CAN:
  2238.       case ZPAD:
  2239.         err = GetInSync (TRUE, &type);
  2240.         if (!err && type == ZACK)
  2241.           break;
  2242.         /* zcrce - dinna wanna starta ping-pong game */
  2243.         SendData (Buffer, 0, ZCRCE);
  2244.         goto gotack;
  2245.       case XOFF:                /* Wait a while for an XON */
  2246.       case XOFF | 0x80:
  2247.         ReadByte (&c, 100);
  2248.       default:
  2249.         ++junkcount;
  2250.         break;
  2251.       }
  2252.     }
  2253.  
  2254.     /* Make sure to stay in transmit window */
  2255.  
  2256.     if (Txwindow)
  2257.     {
  2258.       while ((Mark - Lrxpos) >= Txwindow)
  2259.       {
  2260.         if (e != ZCRCQ)
  2261.           SendData (Buffer, 0, e = ZCRCQ);
  2262.         err = GetInSync (TRUE, &type);
  2263.         if (err || type != ZACK)
  2264.         {
  2265.           SendData (Buffer, 0, ZCRCE);
  2266.           goto gotack;
  2267.         }
  2268.       }
  2269.     }
  2270.   }
  2271.   while (!eofseen);
  2272.  
  2273.   /* Send ZEOF header, wait for ZRINIT header from receiver */
  2274.  
  2275.   for (;;)
  2276.   {
  2277.     hdr.position = Mark;
  2278.     SendBinaryHeader (ZEOF, &hdr);
  2279.     if (GetInSync (FALSE, &type))
  2280.       return ERROR;
  2281.     switch (type)
  2282.     {
  2283.     case ZRINIT:
  2284.     case ZSKIP:
  2285.       return FINE;
  2286.     case ZACK:
  2287.       break;
  2288.     case ZRPOS:
  2289.       goto somemore;
  2290.     default:
  2291.       return ERROR;
  2292.     }
  2293.   }
  2294. }
  2295.  
  2296. /* ----- Invite receiver ----------------------------------------------- */
  2297.  
  2298. STATIC WORD
  2299. Invitation ()
  2300. {
  2301.   HEADER hdr;
  2302.   WORD retry;
  2303.   WORD err;
  2304.  
  2305.   DB (kprintf ("»Invitation«\n"));
  2306.  
  2307.   for (retry = 0; retry <= ZRetries; ++retry)
  2308.   {
  2309.     UpdateRetries(retry);
  2310.     hdr.position = 0;
  2311.     SendHexHeader (ZRQINIT, &hdr);
  2312.     if ((err = WaitZPAD (ZTimeout, 10)) == FINE)
  2313.     {
  2314.       DB (kprintf ("»Got ZPad«\n"));
  2315.       return FINE;
  2316.     }
  2317.   }
  2318.   return TIMEOUT;
  2319. }
  2320.  
  2321. /* ----- Send invitation to receiver, get receiver's parameters -------- */
  2322.  
  2323. STATIC WORD
  2324. SessionStartup ()
  2325. {
  2326.   WORD retry;
  2327.   WORD err;
  2328.   UBYTE *message;
  2329.   UBYTE type;
  2330.   HEADER hdr;
  2331.  
  2332.   DB (kprintf ("»SessionStartup«\n"));
  2333.  
  2334.   StartupTime = GetTime ();
  2335.   ResetUI(handle);
  2336.   ZBytesIn = ZBytesOut = LastBPS = 0;
  2337.   message = NULL;
  2338.   for (retry = 0; retry <= ZRetries; ++retry)
  2339.   {
  2340.     if(message)
  2341.     {
  2342.       AddMessage(message);
  2343.       message = NULL;
  2344.     }
  2345.  
  2346.     UpdateRetries(retry);
  2347.     switch (GetHeader (&type, &hdr))
  2348.     {
  2349.     case FINE:
  2350.       DB (kprintf ("»FINE: type %ld«\n", type));
  2351.  
  2352.       switch (type)
  2353.       {
  2354.       case ZRINIT:              /* This is what we want */
  2355.         message = (UBYTE *) "ZRINIT";
  2356.  
  2357.         DB (kprintf ("»got ZRInit«\n"));
  2358.  
  2359.         /* Set transfer parameters */
  2360.  
  2361.         Rxbuflen = hdr.zrinit.bufsize;
  2362.         Txwindow = ZWindow;
  2363.         Txwspac = Zcrcq;
  2364.         Count = ZPacket;
  2365. #ifdef XXX
  2366.         if (Txwindow < 256)
  2367.           Txwindow = 256;
  2368.         Txwindow = (Txwindow / 64) * 64;
  2369.         Txwspac = Txwindow / 4;
  2370.         if (Count > Txwspac ||
  2371.             (!Count && Txwspac < ZMAXSPLEN))
  2372.           Count = Txwspac;
  2373. #endif
  2374.         if (!hdr.zrinit.canfdx)
  2375.           Txwindow = 0;
  2376.         if (Rxbuflen && Count > Rxbuflen)
  2377.           Count = Rxbuflen;
  2378.  
  2379.         /* No need to send an ZSINIT frame if ... */
  2380.  
  2381.         Zctlesc = hdr.zrinit.escctl || Escape ();
  2382.         if (!Escape () || hdr.zrinit.escctl)
  2383.         {
  2384.           err = FINE;
  2385.           goto done;
  2386.         }
  2387.         for (retry = 0; retry <= ZRetries; ++retry)
  2388.         {
  2389.           DB (kprintf ("»sending ZSInit\n«"));
  2390.           /* ZSINIT header */
  2391.           hdr.position = 0;
  2392.           hdr.zsinit.escctl = 1;
  2393.           SendBinaryHeader (ZSINIT, &hdr);
  2394.  
  2395.           /* Our attention string (empty string) */
  2396.           type = 0;
  2397.           SendData (&type, 1, ZCRCW);
  2398.           /* Wait for ZACK header from receiver */
  2399.           if (!GetHeader (&type, &hdr) && type == ZACK)
  2400.           {
  2401.             err = FINE;
  2402.             goto done;
  2403.           }
  2404.         }
  2405.         err = TIMEOUT;
  2406.         goto done;
  2407.  
  2408.       case ZCHALLENGE:          /* Echo challenge number */
  2409.         SendHexHeader (ZACK, &hdr);
  2410.         message = (UBYTE *) "ZCHALLENGE";
  2411.         break;
  2412.       case ZCOMMAND:            /* They didn't see out ZRQINIT */
  2413.         hdr.position = 0;
  2414.         SendHexHeader (ZRQINIT, &hdr);
  2415.         message = (UBYTE *) "ZRQINIT";
  2416.         break;
  2417.       case ZRQINIT:
  2418.         if (!hdr.zrqinit.command)
  2419.           break;                /* Ignore (our echo) */
  2420.         /* Fall thru */
  2421.       default:
  2422.         hdr.position = 0;
  2423.         SendHexHeader (ZNAK, &hdr);
  2424.         message = (UBYTE *) "->ZNAK";
  2425.         break;
  2426.       }
  2427.     case TIMEOUT:
  2428.       err = TIMEOUT;
  2429.       goto done;
  2430.     default:
  2431.       hdr.position = 0;
  2432.       SendHexHeader (ZNAK, &hdr);
  2433.       message = (UBYTE *) "->ZNAK";
  2434.       break;
  2435.     }
  2436.   }
  2437.   err = TIMEOUT;                /* Retry count exhausted */
  2438. done:
  2439.   DB (kprintf ("»Done«\n"));
  2440.   if(message)
  2441.     AddMessage(message);
  2442.   return err;
  2443. }
  2444.  
  2445. /* ----- Send file information ----------------------------------------- */
  2446.  
  2447. STATIC WORD
  2448. SendFileInfo (
  2449.                UBYTE * name,            /* File name */
  2450.                ULONG size,              /* File size */
  2451.                BOOL text,               /* Text file? */
  2452.                ULONG modif,             /* Modification date */
  2453.                ULONG mode,              /* File mode data */
  2454.                ULONG * startpos,        /* Start position */
  2455.                ULONG TotalFiles,        /* Number of files to send */
  2456.                ULONG TotalBytes)        /* Number of bytes to transmit */
  2457. {
  2458.   UBYTE *p;
  2459.   UBYTE s[30];
  2460.   WORD retry;
  2461.   WORD err;
  2462.   UBYTE *message;
  2463.   UBYTE buffer[128];
  2464.   UBYTE type;
  2465.   HEADER hdr;
  2466.   LONG len;
  2467.  
  2468.   if (!ZSendFullPath)
  2469.     name = FilePart (name);
  2470.   else
  2471.   {
  2472.     LONG i;
  2473.  
  2474.     /* Follow Unix file name conventions, i.e. strip the
  2475.        * leading device/volume name if any.
  2476.      */
  2477.  
  2478.     for (i = 0; i < strlen (name); i++)
  2479.     {
  2480.       if (name[i] == ':')
  2481.       {
  2482.         name = &name[i + 1];
  2483.         break;
  2484.       }
  2485.     }
  2486.   }
  2487.  
  2488.   /* File name as zero terminated ASCII string */
  2489.  
  2490.   p = buffer;
  2491.   strcpy (p, name);
  2492.   p += strlen (name) + 1;
  2493.  
  2494.   /* File size as decimal ASCII digits */
  2495.  
  2496.   LimitedSPrintf (sizeof (s), s, "%ld", size);
  2497.   len = strlen (s);
  2498.  
  2499.   memcpy (p, s, len);
  2500.   p += len;
  2501.   *p++ = ' ';
  2502.  
  2503.   /* Modification date: octal string = seconds since 1/1/1970 UTC */
  2504.   ConvertTime (FALSE, &modif);  /* -> ZModem time */
  2505.  
  2506.   ToOctal (modif, s);
  2507.   len = strlen (s);
  2508.   memcpy (p, s, len);
  2509.   p += len;
  2510.   *p++ = ' ';
  2511.  
  2512.   /* File mode (octal string) */
  2513.  
  2514.   ToOctal (mode, s);
  2515.   len = strlen (s);
  2516.   memcpy (p, s, len);
  2517.   p += len;
  2518.   *p++ = ' ';
  2519.  
  2520.   /* Serial number (octal string) */
  2521.  
  2522.   *p++ = '0';
  2523.   *p++ = ' ';
  2524.  
  2525.   /* Number of files remaining (decimal number) */
  2526.  
  2527.   LimitedSPrintf (sizeof (s), s, "%ld", TotalFiles);
  2528.   len = strlen (s);
  2529.   memcpy (p, s, len);
  2530.   p += len;
  2531.   *p++ = ' ';
  2532.  
  2533.   /* Number of bytes remaining (decimal number) */
  2534.   LimitedSPrintf (sizeof (s), s, "%ld", TotalBytes);
  2535.   len = strlen (s);
  2536.   memcpy (p, s, len);
  2537.   p += len;
  2538.   *p++ = 0;                     /* Final NULL */
  2539.  
  2540.   /* Send ZFILE header with ZModem conversion, management and transport
  2541.      options followed by a ZCRCW data subpacket containing the file name,
  2542.      file length, modification date. */
  2543.  
  2544.   message = NULL;
  2545.   for (retry = 0; retry <= ZRetries; ++retry)
  2546.   {
  2547.     UpdateRetries(retry);
  2548.  
  2549.     hdr.position = 0;
  2550.     hdr.zfile.conv = text ? 0 : ZCBIN;
  2551.  
  2552.     ConvertNL = text;
  2553.  
  2554.     if (ZOverwrite)
  2555.       hdr.zfile.manage = ZMCLOB;        /* Replace existing file */
  2556.  
  2557.     DB (kprintf ("»Sending ZFILE header (flags %08lx)«\n", hdr.position));
  2558.     SendBinaryHeader (ZFILE, &hdr);
  2559.     DB (kprintf ("»Sending data«\n"));
  2560.     SendData (buffer, p - buffer, ZCRCW);
  2561.     DB (kprintf ("»waiting for response«\n"));
  2562.   again:
  2563.     if(message)
  2564.     {
  2565.       AddMessage(message);
  2566.       message = NULL;
  2567.     }
  2568.  
  2569.     if (GetHeader (&type, &hdr))
  2570.     {                           /* Error */
  2571.       continue;
  2572.     }
  2573.     switch (type)
  2574.     {
  2575.     case ZRINIT:
  2576.       message = (UBYTE *) "ZRINIT";
  2577.       if (WaitZPAD (ZTimeout, 10) == FINE)
  2578.         goto again;
  2579.       break;
  2580.     case ZSKIP:
  2581.       message = (UBYTE *) "ZSKIP";
  2582.       /* The receiver may respond with a ZSKIP header, which
  2583.          makes the sender proceed to the next file (if any) in the
  2584.          batch. */
  2585.       *startpos = 0x7FFFFFFF;
  2586.       err = FINE;
  2587.       goto done;
  2588.     case ZRPOS:
  2589.       message = (UBYTE *) "ZRPOS";
  2590.       *startpos = hdr.position;
  2591.       err = FINE;
  2592.       goto done;
  2593.     }
  2594.   }
  2595.   err = TIMEOUT;
  2596. done:
  2597.   if(message)
  2598.     AddMessage(message);
  2599.  
  2600.   return err;
  2601. }
  2602.  
  2603. /* ----- Session cleanup ----------------------------------------------- */
  2604.  
  2605. STATIC VOID
  2606. SessionCleanup ()
  2607. {
  2608.   WORD retry;
  2609.   HEADER hdr;
  2610.   UBYTE type;
  2611.   STATIC UBYTE oo[] = "OO";     /* Over and out */
  2612.  
  2613.   for (retry = 0; retry <= ZRetries; ++retry)
  2614.   {
  2615.     hdr.position = 0;
  2616.     SendHexHeader (ZFIN, &hdr);
  2617.     if (GetHeader (&type, &hdr))
  2618.       continue;
  2619.     if (type == ZFIN)
  2620.     {
  2621.       SendString (oo, strlen (oo));
  2622.       return;
  2623.     }
  2624.   }
  2625. }
  2626.  
  2627. /* ----- Send a file --------------------------------------------------- */
  2628.  
  2629. LONG
  2630. ZTransmit (struct WBArg * ArgList, LONG NumArgs, BOOL TextMode)
  2631. {
  2632.   LONG err;
  2633.   ULONG modif;
  2634.   ULONG mode;
  2635.   ULONG TotalFiles, TotalBytes;
  2636.   BPTR OldCD, FileLock;
  2637.   LONG i;
  2638.   D_S (struct FileInfoBlock, FileInfo);
  2639.  
  2640.   Transmitting = TRUE;
  2641.  
  2642.   TotalFiles = TotalBytes = 0;
  2643.   BytesThisBatch = FilesThisBatch = 0;
  2644.  
  2645.   for (i = 0; i < NumArgs; i++)
  2646.   {
  2647.     OldCD = CurrentDir (ArgList[i].wa_Lock);
  2648.  
  2649.     if (!(FileLock = Lock (ArgList[i].wa_Name, SHARED_LOCK)))
  2650.       err = IoErr ();
  2651.     else
  2652.     {
  2653.       if (!Examine (FileLock, FileInfo))
  2654.         err = IoErr ();
  2655.       else
  2656.       {
  2657.         if (FileInfo->fib_DirEntryType > 0)
  2658.           err = ERROR_OBJECT_WRONG_TYPE;
  2659.         else
  2660.         {
  2661.           err = 0;
  2662.  
  2663.           if (!ZSkipIfArchived || !(FileInfo->fib_Protection & FIBF_ARCHIVE))
  2664.           {
  2665.             TotalFiles++;
  2666.             TotalBytes += FileInfo->fib_Size;
  2667.           }
  2668.         }
  2669.       }
  2670.  
  2671.       UnLock (FileLock);
  2672.     }
  2673.  
  2674.     CurrentDir (OldCD);
  2675.  
  2676.     if (err)
  2677.       return (err);
  2678.   }
  2679.  
  2680.   DB (kprintf ("»Buffer size is %ld«\n", ZMAXSPLEN));
  2681.  
  2682.   Buffer = (UBYTE *) AllocVecPooled (ZMAXSPLEN, MEMF_ANY);
  2683.   err = NewTxBuffer ();
  2684.   handle = OpenUI ("ZModem transmit");
  2685.  
  2686.   if (Buffer != NULL && err == 0 && handle != NULL)
  2687.   {
  2688.     Zctlesc = Escape ();
  2689.  
  2690.     ClearSerial ();
  2691.  
  2692.     if (SerialCache = GetSerialWaiting ())
  2693.     {
  2694.       if (SerialCache > SERIALBUFFERSIZE)
  2695.         SerialCache = SERIALBUFFERSIZE;
  2696.  
  2697.       DoSerialRead (SerialBuffer, SerialCache);
  2698.     }
  2699.  
  2700.     StartSerialRead (&zc, 1);
  2701.  
  2702.     for (i = 0; i < NumArgs; i++)
  2703.     {
  2704.       OldCD = CurrentDir (ArgList[i].wa_Lock);
  2705.  
  2706.       FileHandle = NULL;
  2707.  
  2708.       if (!(FileLock = Lock (ArgList[i].wa_Name, SHARED_LOCK)))
  2709.         err = IoErr ();
  2710.       else
  2711.       {
  2712.         if (!Examine (FileLock, FileInfo))
  2713.           err = IoErr ();
  2714.         else
  2715.         {
  2716.           if (FileInfo->fib_DirEntryType > 0)
  2717.             err = ERROR_OBJECT_WRONG_TYPE;
  2718.           else
  2719.           {
  2720.             LimitedStrcpy (sizeof (FileName), FileName, ArgList[i].wa_Name);
  2721.  
  2722.             if (!(FileHandle = Open (FileName, MODE_OLDFILE)))
  2723.               err = IoErr ();
  2724.             else
  2725.               err = 0;
  2726.           }
  2727.         }
  2728.  
  2729.         UnLock (FileLock);
  2730.       }
  2731.  
  2732.       CurrentDir (OldCD);
  2733.  
  2734.       if (ZSkipIfArchived && (FileInfo->fib_Protection & FIBF_ARCHIVE))
  2735.         continue;
  2736.  
  2737.       if (err != 0 || FileHandle == NULL)
  2738.         break;
  2739.       else
  2740.       {
  2741.         StartupTime = GetTime ();
  2742.         ZBytesIn = ZBytesOut = LastBPS = 0;
  2743.         Mark = StartPos = 0;
  2744.         ResetUI(handle);
  2745.  
  2746.         SetVBuf (FileHandle, NULL, BUF_FULL, 4096);
  2747.  
  2748.         Length = FileInfo->fib_Size;
  2749.         modif = (FileInfo->fib_Date.ds_Days * 24 * 60 + FileInfo->fib_Date.ds_Minute) * 60 + FileInfo->fib_Date.ds_Tick / TICKS_PER_SECOND;
  2750.         mode = Amiga2UnixMode (FileInfo->fib_Protection);
  2751.  
  2752.         UpdateProgress (0, Length);
  2753.  
  2754.         if (err = setjmp (CancelEnv))
  2755.         {                       /* Cancel or abort */
  2756.           if (err == CANCEL)
  2757.             SendCancel ();      /* Send cancel string */
  2758.  
  2759.           break;
  2760.         }
  2761.  
  2762.         if (i == 0)
  2763.         {
  2764.           if (!(err = Invitation ()))
  2765.             err = SessionStartup ();
  2766.         }
  2767.         else
  2768.           err = 0;
  2769.  
  2770.         if (!err)
  2771.         {
  2772.           if (!(err = SendFileInfo (FileName, Length, TextMode, modif, mode, &Mark, TotalFiles, TotalBytes)))
  2773.           {
  2774.             UpdateBatch(FilesThisBatch,TotalFiles,BytesThisBatch,TotalBytes);
  2775.  
  2776.             TotalFiles--;
  2777.             TotalBytes -= Length;
  2778.  
  2779.             BytesThisBatch += Length;
  2780.             FilesThisBatch++;
  2781.  
  2782.             if (Mark != 0x7FFFFFFF)
  2783.             {                   /* Not skip */
  2784.               SetFilePosition (Mark);
  2785.  
  2786.               NameProgress (FilePart (FileName));
  2787.  
  2788.               /* Send file data */
  2789.               err = SendFileData ();
  2790.             }
  2791.           }
  2792.         }
  2793.  
  2794.         Close (FileHandle);
  2795.         FileHandle = NULL;
  2796.  
  2797.         if (err == 0)
  2798.         {
  2799.           if (ZDeleteAfterSending)
  2800.             DeleteFile (FileName);
  2801.           else
  2802.           {
  2803.             if (ZProtectAfterSending)
  2804.               SetProtection (FileName, FileInfo->fib_Protection | FIBF_ARCHIVE);
  2805.           }
  2806.         }
  2807.  
  2808.         if (TotalFiles == 0)
  2809.           SessionCleanup ();
  2810.  
  2811.         if (err != 0)
  2812.           break;
  2813.       }
  2814.     }
  2815.  
  2816.     StopSerialRead ();
  2817.     RestartSerial ();
  2818.   }
  2819.  
  2820.   if (handle)
  2821.   {
  2822.     UpdateError (err);
  2823.  
  2824.     CloseUI (handle, err);
  2825.     handle = NULL;
  2826.   }
  2827.   if (FileHandle)
  2828.   {
  2829.     Close (FileHandle);
  2830.     FileHandle = NULL;
  2831.   }
  2832.  
  2833.   DisposeTxBuffer ();
  2834.  
  2835.   if (Buffer)
  2836.     FreeVecPooled (Buffer);
  2837.  
  2838.   return err;
  2839. }
  2840.  
  2841. /* ===== RECEIVE ======================================================= */
  2842.  
  2843. /* ----- Free bytes on the current file system ------------------------- */
  2844.  
  2845. STATIC ULONG
  2846. GetFreeSpace ()
  2847. {
  2848.   if (IsBlockMapped)
  2849.   {
  2850.     D_S (struct InfoData, InfoData);
  2851.  
  2852.     if (Info (ReceiveDir, InfoData))
  2853.     {
  2854.       if (InfoData->id_NumBlocks && InfoData->id_BytesPerBlock)
  2855.         return ((ULONG) ((InfoData->id_NumBlocks - InfoData->id_NumBlocksUsed) * InfoData->id_BytesPerBlock));
  2856.     }
  2857.   }
  2858.  
  2859.   return (0xFFFFFFFF);          /* many free bytes ... */
  2860. }
  2861.  
  2862. /* ----- Ack a ZFIN packet, let byegones be byegones ------------------- */
  2863.  
  2864. STATIC VOID
  2865. AckOverAndOut ()
  2866. {
  2867.   WORD retry;
  2868.   WORD err;
  2869.   HEADER hdr;
  2870.   UBYTE c;
  2871.  
  2872.   hdr.position = 0;
  2873.   for (retry = 0; retry < 3; ++retry)
  2874.   {
  2875.     SendHexHeader (ZFIN, &hdr);
  2876.  
  2877.     err = ReadByte (&c, ZTimeout);
  2878.  
  2879.     if (err == FINE && c == 'O')
  2880.     {
  2881.       ReadByte (&c, 60);
  2882.       return;                   /* Over and Out */
  2883.     }
  2884.   }
  2885. }
  2886.  
  2887. /* ----- Initialize for Zmodem receive attempt ------------------------- */
  2888.  
  2889. STATIC WORD
  2890. TryZReceive (
  2891.               HEADER * filehdr) /* Return sender's options */
  2892. {
  2893.   UBYTE *message;               /* Message to display */
  2894.   HEADER hdr;                   /* Header */
  2895.   UBYTE type;                   /* Header type received */
  2896.   WORD retry;                   /* Retry counter */
  2897.   WORD err;                     /* Error code */
  2898.  
  2899.   for (retry = 0, message = NULL; retry <= ZRetries;)
  2900.   {
  2901.     UpdateRetries(retry);
  2902.  
  2903.     hdr.position = 0;
  2904.     if (TryZhdrType == ZRINIT)
  2905.     {                           /* Capability flags */
  2906.       hdr.zrinit.bufsize = ZBuffer;
  2907.       hdr.zrinit.canfdx = 1;
  2908.       hdr.zrinit.canovio = 1;
  2909.       hdr.zrinit.escctl = Escape ();
  2910.     }
  2911.     SendHexHeader (TryZhdrType, &hdr);
  2912.     if (TryZhdrType == ZSKIP)   /* Don't skip too far */
  2913.       TryZhdrType = ZRINIT;
  2914.   again:
  2915.     if(message)
  2916.     {
  2917.       AddMessage(message);
  2918.       message = NULL;
  2919.     }
  2920.     ++retry;
  2921.     if (GetHeader (&type, &hdr) == FINE)
  2922.     {
  2923.       DB (kprintf ("»FINE: type %ld«\n", type));
  2924.  
  2925.       switch (type)
  2926.       {
  2927.       case ZRQINIT:
  2928.         continue;
  2929.  
  2930.       case ZFILE:
  2931.         *filehdr = hdr;         /* Return file options */
  2932.         TryZhdrType = ZRINIT;
  2933.         if (!ReceiveData (Buffer, ZMAXSPLEN, &Count, &type) &&
  2934.             type == ZCRCW)
  2935.         {
  2936.           err = FINE;
  2937.           goto done;            /* File name received */
  2938.         }
  2939.         SendHexHeader (ZNAK, &hdr);
  2940.         message = (UBYTE *) "ZFILE error";
  2941.         goto again;
  2942.       case ZSINIT:
  2943.         Zctlesc = hdr.zsinit.escctl;
  2944.         if (!ReceiveData (Attn, ZATTNLEN, &AttnLen, &type) &&
  2945.             type == ZCRCW)
  2946.         {
  2947.           hdr.position = 1;
  2948.           SendHexHeader (ZACK, &hdr);
  2949.           message = (UBYTE *) "ZSINIT ok";
  2950.           goto again;
  2951.         }
  2952.         SendHexHeader (ZNAK, &hdr);
  2953.         message = (UBYTE *) "ZSINIT error";
  2954.         goto again;
  2955.       case ZFREECNT:
  2956.         hdr.position = GetFreeSpace ();
  2957.         SendHexHeader (ZACK, &hdr);
  2958.         message = (UBYTE *) "ZFREECNT";
  2959.         goto again;
  2960.       case ZCOMMAND:
  2961.         if (!ReceiveData (Buffer, ZMAXSPLEN, &Count, &type) && type == ZCRCW)
  2962.         {
  2963.           STRPTR String;
  2964.  
  2965.           String = Buffer;
  2966.  
  2967.           while (*String == ' ' || *String == '\t')
  2968.             String++;
  2969.  
  2970.           if (!Strnicmp (String, "echo", 4))
  2971.             AddMessage(&String[4]);
  2972.           else
  2973.             AddMessage("Ignoring ZCOMMAND %s",String);
  2974.  
  2975.           do
  2976.           {
  2977.             hdr.position = 0;
  2978.             SendHexHeader (ZCOMPL, &hdr);
  2979.  
  2980.             if (GetHeader (&type, &hdr) == ZFIN)
  2981.             {
  2982.               AckOverAndOut ();
  2983.               Count = 0;        /* Nothing in Buffer */
  2984.               err = FINE;
  2985.               goto done;
  2986.             }
  2987.           }
  2988.           while (++errors < ZErrors);
  2989.         }
  2990.         else
  2991.         {
  2992.           SendHexHeader (ZNAK, &hdr);
  2993.           message = (UBYTE *) "ZCOMMAND error";
  2994.         }
  2995.  
  2996.         goto again;
  2997.       case ZCOMPL:
  2998.         message = (UBYTE *) "ZCOMPL";
  2999.         goto again;
  3000.       case ZFIN:
  3001.         message = (UBYTE *)"ZFIN";
  3002.         AckOverAndOut ();
  3003.         Count = 0;              /* Nothing in Buffer */
  3004.         err = FINE;
  3005.         goto done;
  3006.       }
  3007.     }
  3008.     else
  3009.       message = NULL;
  3010.   }
  3011.   err = TIMEOUT;
  3012.   Count = 0;                    /* Nothing in Buffer */
  3013. done:
  3014.   if(message)
  3015.      AddMessage(message);
  3016.   return err;
  3017. }
  3018.  
  3019. /* ----- Process incoming file information header ---------------------- */
  3020.  
  3021. STATIC LONG
  3022. ProcedeHeader (
  3023.                 HEADER * filehdr,       /* Sender's options */
  3024.                 STRPTR name,            /* File name extracted */
  3025.                 ULONG * length,         /* File length extracted */
  3026.                 ULONG * modif,          /* Modification time extracted */
  3027.                 ULONG * mode,           /* File mode extracted */
  3028.                 ULONG * filesleft,      /* Number of files left (0 == not known) */
  3029.                 ULONG * bytesleft)      /* Number of bytes left (0 == not known) */
  3030. {
  3031.   WORD err;
  3032.   UBYTE *p, *max;
  3033.   UBYTE c;
  3034.   BOOL exists;                  /* File exists */
  3035.   LONG len;
  3036.   BPTR fileLock;
  3037.   D_S (struct FileInfoBlock, FileInfo);
  3038.  
  3039.   *name = 0;
  3040.   *length = 0;
  3041.   *modif = 0;
  3042.   *mode = 0;
  3043.   *filesleft = 0;
  3044.   *bytesleft = 0;
  3045.  
  3046.   FileHandle = NULL;
  3047.  
  3048.   /* Extract fields from file information header */
  3049.  
  3050.   max = (p = Buffer) + Count;
  3051.   err = 0;
  3052.   while (p < max)
  3053.   {
  3054.     c = *p++;
  3055.     switch (err)
  3056.     {
  3057.     case 0:                     /* File name (C-string) */
  3058.       if (c == '\0')
  3059.       {
  3060.         len = p - 1 - Buffer;
  3061.         memcpy (name, Buffer, len);
  3062.         name[len] = 0;
  3063.         ++err;                  /* Goto next state */
  3064.       }
  3065.       break;
  3066.     case 1:                     /* File length (decimal string, space) */
  3067.       if (c == ' ' || c == '\0')
  3068.         ++err;                  /* Goto next state */
  3069.       else
  3070.         *length = *length * 10 + (c & 0x0F);
  3071.       break;
  3072.     case 2:                     /* File modification time (octal string, space) */
  3073.       if (c == ' ' || c == '\0')
  3074.       {
  3075.         ConvertTime (TRUE, modif);      /* -> Amiga time */
  3076.         ++err;                  /* Goto next state */
  3077.       }
  3078.       else
  3079.         *modif = *modif * 8 + (c & 7);
  3080.       break;
  3081.     case 3:                     /* File modes (octal string, space) */
  3082.       if (c == ' ' || c == '\0')
  3083.       {
  3084.         *mode = Unix2AmigaMode (*mode);
  3085.         ++err;                  /* Goto next state */
  3086.       }
  3087.       else
  3088.         *mode = *mode * 8 + (c & 7);
  3089.       break;
  3090.     case 4:                     /* Serial number. */
  3091.       if (c == ' ' || c == '\0')
  3092.         err++;
  3093.       break;
  3094.     case 5:                     /* Number of files left */
  3095.       if (c == ' ' || c == '\0')
  3096.         err++;
  3097.       else
  3098.         *filesleft = *filesleft * 10 + (c & 15);
  3099.       break;
  3100.     case 6:                     /* Number of bytes left */
  3101.       if (c == ' ' || c == '\0')
  3102.         err++;
  3103.       else
  3104.         *bytesleft = *bytesleft * 10 + (c & 15);
  3105.       break;
  3106.     default:
  3107.       break;
  3108.     }
  3109.   }
  3110.  
  3111.   if (err < 3)                  /* Got no modification time...  */
  3112.     *modif = GetTime ();        /* ...so use actual time        */
  3113.  
  3114.   if (!ZUseFullPath)
  3115.     name = FilePart (name);
  3116.  
  3117.   /* See if file exists */
  3118.  
  3119.   if (fileLock = Lock (name, SHARED_LOCK))
  3120.   {
  3121.     if (!Examine (fileLock, FileInfo))
  3122.       memset (FileInfo, 0, sizeof (struct FileInfoBlock));
  3123.  
  3124.     UnLock (fileLock);
  3125.  
  3126.     exists = TRUE;
  3127.   }
  3128.   else
  3129.     exists = FALSE;
  3130.  
  3131.   /* Skip file if not present at rx */
  3132.  
  3133.   if (filehdr->zfile.skip && !exists)
  3134.     return 100;
  3135.  
  3136.   if (exists)
  3137.   {
  3138.     if (ZTransferIfNewer && err > 2)
  3139.     {
  3140.       struct DateStamp Stamp;
  3141.  
  3142.       Stamp.ds_Days = *modif / (24 * 60 * 60);
  3143.       Stamp.ds_Minute = (*modif % (24 * 60 * 60)) / 60;
  3144.       Stamp.ds_Tick = (*modif % 60) * TICKS_PER_SECOND;
  3145.  
  3146.       if (CompareDates (&Stamp, &FileInfo->fib_Date) < 0)
  3147.         exists = FALSE;
  3148.     }
  3149.  
  3150.     if (ZTransferIfSize && err > 1)
  3151.     {
  3152.       if (FileInfo->fib_Size != *length)
  3153.         exists = FALSE;
  3154.     }
  3155.   }
  3156.  
  3157.   /* Delete existing file */
  3158.  
  3159.   if (exists && filehdr->zfile.manage == ZMCLOB)
  3160.     exists = FALSE;
  3161.  
  3162.   /* Create new file */
  3163.  
  3164.   if (!exists)
  3165.     FileHandle = Open (name, MODE_NEWFILE);
  3166.   else
  3167.   {
  3168.     if (!(fileLock = Lock (name, EXCLUSIVE_LOCK)))
  3169.       FileHandle = Open (name, MODE_NEWFILE);
  3170.     else
  3171.     {
  3172.       if (!(FileHandle = OpenFromLock (fileLock)))
  3173.       {
  3174.         UnLock (fileLock);
  3175.  
  3176.         FileHandle = Open (name, MODE_READWRITE);
  3177.       }
  3178.  
  3179.       if (FileHandle)
  3180.       {
  3181.         if (Seek (FileHandle, 0, OFFSET_END) == -1)
  3182.         {
  3183.           LONG Error;
  3184.  
  3185.           Error = IoErr ();
  3186.  
  3187.           Close (FileHandle);
  3188.  
  3189.           SetIoErr (Error);
  3190.  
  3191.           FileHandle = NULL;
  3192.         }
  3193.       }
  3194.     }
  3195.   }
  3196.  
  3197.   if (FileHandle)
  3198.   {
  3199.     LimitedStrcpy (sizeof (FileName), FileName, name);
  3200.  
  3201.     return (FINE);
  3202.   }
  3203.   else
  3204.     return (IoErr ());
  3205. }
  3206.  
  3207. /* ----- Write buffer to file ------------------------------------------ */
  3208.  
  3209. STATIC LONG
  3210. WriteFile ()
  3211. {
  3212.   if (Write (FileHandle, Buffer, Count) == Count)
  3213.     return (0);
  3214.   else
  3215.     return (IoErr ());
  3216. }
  3217.  
  3218. /* ----- File was completely received ----------------------------------- */
  3219.  
  3220. STATIC VOID
  3221. CleanupFile (
  3222.               STRPTR name,      /* File name from ZModem header */
  3223.               ULONG modif,      /* File modification time */
  3224.               ULONG mode)       /* File mode data */
  3225. {
  3226.   struct DateStamp date;
  3227.  
  3228.   /* Set file creation/modification time and file attributes */
  3229.  
  3230.   date.ds_Days = modif / (24 * 60 * 60);
  3231.   date.ds_Minute = (modif % (24 * 60 * 60)) / 60;
  3232.   date.ds_Tick = (modif % 60) * TICKS_PER_SECOND;
  3233.  
  3234.   SetFileDate (name, &date);
  3235.   SetProtection (name, mode);
  3236. }
  3237.  
  3238. /* ----- Receive a file ------------------------------------------------ */
  3239.  
  3240. STATIC WORD
  3241. ReceiveFile (
  3242.               HEADER * filehdr) /* Sender's options */
  3243. {
  3244.   WORD err;             /* Error code */
  3245.   WORD retry;           /* Retry counter */
  3246.   UBYTE *message;       /* Message to display */
  3247.   UBYTE name[256];      /* File name from Z header */
  3248.   ULONG modif;          /* File modification date (Amiga time) */
  3249.   ULONG mode;           /* File mode data. */
  3250.   ULONG filesleft;      /* Number of files left. */
  3251.   ULONG bytesleft;      /* Number of bytes left. */
  3252.   HEADER hdr;           /* Header */
  3253.   UBYTE type;           /* Header type received */
  3254.   UBYTE end;            /* Data frame type */
  3255.  
  3256.   /* Get ready to receive new file */
  3257.  
  3258.   if (ProcedeHeader (filehdr, name, &Length, &modif, &mode, &filesleft, &bytesleft))
  3259.   {
  3260.     /* Error or skip, but that's fine! */
  3261.     TryZhdrType = ZSKIP;
  3262.     return FINE;
  3263.   }
  3264.  
  3265.   UpdateBatch(FilesThisBatch,filesleft,BytesThisBatch,bytesleft);
  3266.  
  3267.   Mark = StartPos = Seek (FileHandle, 0, OFFSET_CURRENT);
  3268.  
  3269.   NameProgress (name);
  3270.   Rxbuflen = ZBuffer;
  3271.  
  3272.   /* Receiving loop */
  3273.  
  3274.   retry = 0;
  3275.   message = NULL;
  3276.  
  3277.   for (;;)
  3278.   {
  3279.     UpdateRetries(retry);
  3280.     UpdateProgress (Mark,Length);
  3281.  
  3282.     hdr.position = Mark;
  3283.     SendHexHeader (ZRPOS, &hdr);
  3284.   nxthdr:
  3285.     if(message)
  3286.     {
  3287.       AddMessage("%s (position = %lD)",message,Mark);
  3288.       message = NULL;
  3289.     }
  3290.     switch (GetHeader (&type, &hdr))
  3291.     {
  3292.     case FINE:
  3293.       switch (type)
  3294.       {
  3295.       case ZNAK:
  3296.         ++retry;
  3297.         if (retry > ZRetries)
  3298.         {
  3299.           err = ERROR;
  3300.           goto done;
  3301.         }
  3302.         message = (UBYTE *) "hdr-ZNAK";
  3303.         break;
  3304.       case ZFILE:
  3305.         err = ReceiveData (Buffer, ZMAXSPLEN, &Count, &end);
  3306.  
  3307.         if (err != FINE)
  3308.           goto done;
  3309.  
  3310.         message = (UBYTE *) "hdr-ZFILE";
  3311.         break;
  3312.       case ZEOF:
  3313.         /* Ignore eof if it's at wrong place - force a
  3314.            timeout because the eof might have gone out before
  3315.            we sent our zrpos. */
  3316.         message = (UBYTE *) "hdr-ZEOF";
  3317.         if (hdr.position != Mark)
  3318.           goto nxthdr;          /* Ignore this eof */
  3319.  
  3320.         Close (FileHandle);
  3321.         FileHandle = NULL;
  3322.  
  3323.         BytesThisBatch += Mark;
  3324.         FilesThisBatch++;
  3325.         CleanupFile (name, modif, mode);
  3326.         err = FINE;             /* Normal eof */
  3327.         goto done;
  3328.       case ZSKIP:
  3329.         message = (UBYTE *) "hdr-ZSKIP";
  3330.         Close (FileHandle);
  3331.         FileHandle = NULL;
  3332.         err = FINE;             /* Sender skipped it */
  3333.         goto done;
  3334.       case ZDATA:
  3335.         message = (UBYTE *) "hdr-ZDATA";
  3336.         if (hdr.position != Mark)
  3337.         {
  3338.           ++retry;
  3339.           if (retry > ZRetries)
  3340.           {
  3341.             err = ERROR;
  3342.             goto done;
  3343.           }
  3344.           SendAttn (Attn);
  3345.           break;
  3346.         }
  3347.       moredata:
  3348.         UpdateProgress (Mark,Length);
  3349.         if(message)
  3350.         {
  3351.           AddMessage("%s (position = %lD)",message,Mark);
  3352.           message = NULL;
  3353.         }
  3354.         switch (ReceiveData (Buffer, ZMAXSPLEN, &Count, &end))
  3355.         {
  3356.         case TIMEOUT:
  3357.           ++retry;
  3358.           if (retry > ZRetries)
  3359.           {
  3360.             err = ERROR;
  3361.             goto done;
  3362.           }
  3363.           message = (UBYTE *) "data-TIMEOUT";
  3364.           break;
  3365.         case ERROR:
  3366.           ++retry;
  3367.           if (retry > ZRetries)
  3368.           {
  3369.             err = ERROR;
  3370.             goto done;
  3371.           }
  3372.           SendAttn (Attn);
  3373.           message = (UBYTE *) "data-ERROR";
  3374.           break;
  3375.         case FINE:
  3376.           retry = 0;
  3377.           WriteFile ();
  3378.           Mark += Count;
  3379.           ZBytesIn += Count;
  3380.           switch (end)
  3381.           {
  3382.           case ZCRCW:
  3383.             message = (UBYTE *) "data-ZCRCW";
  3384.             hdr.position = Mark;
  3385.             SendHexHeader (ZACK, &hdr);
  3386.             goto nxthdr;
  3387.           case ZCRCQ:
  3388.             message = (UBYTE *) "data-ZCRCQ";
  3389.             hdr.position = Mark;
  3390.             SendHexHeader (ZACK, &hdr);
  3391.             goto moredata;
  3392.           case ZCRCG:
  3393.             message = (UBYTE *) "data-ZCRCG";
  3394.             goto moredata;
  3395.           case ZCRCE:
  3396.             message = (UBYTE *) "data-ZCRCE";
  3397.             goto nxthdr;
  3398.           }
  3399.           break;
  3400.         }
  3401.         break;
  3402.       default:
  3403.         err = ERROR;
  3404.         goto done;
  3405.       }
  3406.       break;
  3407.     case TIMEOUT:
  3408.       ++retry;
  3409.       if (retry > ZRetries)
  3410.       {
  3411.         err = ERROR;
  3412.         goto done;
  3413.       }
  3414.       message = (UBYTE *) "hdr-TIMEOUT";
  3415.       break;
  3416.     case ERROR:
  3417.       ++retry;
  3418.       if (retry > ZRetries)
  3419.       {
  3420.         err = ERROR;
  3421.         goto done;
  3422.       }
  3423.       SendAttn (Attn);
  3424.       message = (UBYTE *) "hdr-ERROR";
  3425.       break;
  3426.     }
  3427.   }
  3428.  
  3429. done:
  3430.   if(message)
  3431.      AddMessage("%s (position = %lD)",message,Mark);
  3432.   return err;
  3433. }
  3434.  
  3435. /* ----- Receive one or more files ------------------------------------- */
  3436.  
  3437. STATIC WORD
  3438. ReceiveFiles ()
  3439. {
  3440.   WORD err;
  3441.   HEADER filehdr;               /* Sender's options */
  3442.  
  3443.   Mark = StartPos = 0;
  3444.   StartupTime = GetTime ();
  3445.   ZBytesIn = ZBytesOut = LastBPS = 0;
  3446.   Zctlesc = Escape ();
  3447.   TryZhdrType = ZRINIT;
  3448.   Attn[0] = 0;                  /* Clear sender's attention string */
  3449.   AttnLen = 0;
  3450.  
  3451.   for (;;)
  3452.   {
  3453.     StartupTime = GetTime ();
  3454.     ZBytesIn = ZBytesOut = LastBPS = 0;
  3455.     ResetUI(handle);
  3456.  
  3457.     errors = 0;
  3458.     err = TryZReceive (&filehdr);
  3459.  
  3460.     if (!Count)
  3461.     {
  3462.       if (err)
  3463.         UpdateError (err);
  3464.  
  3465.       return err;
  3466.     }
  3467.  
  3468.     ConvertNL = (BOOL) (filehdr.zfile.conv == ZCNL);
  3469.  
  3470.     err = ReceiveFile (&filehdr);
  3471.  
  3472.     UpdateError (err);
  3473.  
  3474.     if (err)
  3475.       return err;
  3476.   }
  3477. }
  3478.  
  3479. /* ----- ZModem receive ------------------------------------------------ */
  3480.  
  3481. LONG
  3482. ZReceive ()
  3483. {
  3484.   struct FileLock *FileLock;
  3485.   struct DosList *Entry;
  3486.   LONG err;
  3487.   BPTR OldCD;
  3488.  
  3489.   Transmitting = FALSE;
  3490.   BytesThisBatch = FilesThisBatch = 0;
  3491.  
  3492.   if (!(ReceiveDir = Lock (Config->PathConfig->BinaryDownloadPath, SHARED_LOCK)))
  3493.     return (IoErr ());
  3494.  
  3495.   FileLock = (struct FileLock *) BADDR (ReceiveDir);
  3496.  
  3497.   IsBlockMapped = FALSE;
  3498.  
  3499.   Entry = LockDosList (LDF_DEVICES | LDF_READ);
  3500.  
  3501.   while (Entry = NextDosEntry (Entry, LDF_DEVICES))
  3502.   {
  3503.     if (Entry->dol_Task == FileLock->fl_Task)
  3504.     {
  3505.       struct FileSysStartupMsg *Startup;
  3506.  
  3507.       Startup = (struct FileSysStartupMsg *) BADDR (Entry->dol_misc.dol_handler.dol_Startup);
  3508.  
  3509.       if (TypeOfMem (Startup))
  3510.       {
  3511.         struct DosEnvec *Environ;
  3512.         STRPTR Name;
  3513.  
  3514.         Name = (STRPTR) BADDR (Startup->fssm_Device);
  3515.         Environ = (struct DosEnvec *) BADDR (Startup->fssm_Environ);
  3516.  
  3517.         if (TypeOfMem (Name) && TypeOfMem (Environ))
  3518.         {
  3519.           struct IOStdReq Request;
  3520.  
  3521.           memset (&Request, 0, sizeof (Request));
  3522.           Request.io_Message.mn_Length = sizeof (Request);
  3523.  
  3524.           if (!OpenDevice (Name + 1, Startup->fssm_Unit, (struct IORequest *) &Request, Startup->fssm_Flags))
  3525.           {
  3526.             IsBlockMapped = TRUE;
  3527.  
  3528.             CloseDevice ((struct IORequest *) &Request);
  3529.  
  3530.             break;
  3531.           }
  3532.         }
  3533.       }
  3534.     }
  3535.   }
  3536.  
  3537.   UnLockDosList (LDF_DEVICES | LDF_READ);
  3538.  
  3539.   OldCD = CurrentDir (ReceiveDir);
  3540.  
  3541.   FileHandle = NULL;            /* No file open yet */
  3542.  
  3543.   DB (kprintf ("»Buffer size is %ld«\n", BUFFERSIZE));
  3544.  
  3545.   Buffer = (UBYTE *) AllocVecPooled (BUFFERSIZE, MEMF_ANY);
  3546.   err = NewTxBuffer ();
  3547.   handle = OpenUI ("ZModem receive");
  3548.  
  3549.   if (Buffer != NULL && err == 0 && handle != NULL)
  3550.   {
  3551.     ClearSerial ();
  3552.  
  3553.     if (SerialCache = GetSerialWaiting ())
  3554.     {
  3555.       if (SerialCache > SERIALBUFFERSIZE)
  3556.         SerialCache = SERIALBUFFERSIZE;
  3557.  
  3558.       DoSerialRead (SerialBuffer, SerialCache);
  3559.     }
  3560.  
  3561.     StartSerialRead (&zc, 1);
  3562.  
  3563.     if (err = setjmp (CancelEnv))       /* Prepare cancel or abort */
  3564.     {
  3565.       if (err == CANCEL)
  3566.         SendCancel ();          /* Send cancel string */
  3567.     }
  3568.     else
  3569.     {
  3570.       StartupTime = GetTime ();
  3571.       ZBytesIn = ZBytesOut = LastBPS = 0;
  3572.       Length = 0;
  3573.       ResetUI(handle);
  3574.  
  3575.       err = ReceiveFiles ();    /* Receive file(s) */
  3576.     }
  3577.  
  3578.     UpdateError (err);
  3579.  
  3580.     StopSerialRead ();
  3581.     RestartSerial ();
  3582.   }
  3583.  
  3584.   if (handle)
  3585.   {
  3586.     CloseUI (handle, err);
  3587.     handle = NULL;
  3588.   }
  3589.  
  3590.   if (FileHandle)
  3591.   {
  3592.     Close (FileHandle);
  3593.     FileHandle = NULL;
  3594.  
  3595.     if (!ZKeepPartial)
  3596.       DeleteFile (FileName);
  3597.   }
  3598.  
  3599.   DisposeTxBuffer ();
  3600.  
  3601.   if (Buffer)
  3602.     FreeVecPooled (Buffer);
  3603.  
  3604.   UnLock (CurrentDir (OldCD));
  3605.  
  3606.   return err;
  3607. }
  3608.  
  3609. /****************************************************************************/
  3610.  
  3611. STATIC VOID
  3612. InternalZModemUpload(BOOL TextMode)
  3613. {
  3614.     UBYTE LocalBuffer[MAX_FILENAME_LENGTH];
  3615.     struct FileRequester *FileRequester;
  3616.  
  3617.     LimitedStrcpy(sizeof(LocalBuffer),LocalBuffer,Config->PathConfig->BinaryUploadPath);
  3618.  
  3619.     if(FileRequester = OpenSeveralFiles(Window,"ZModem transfer","Send",NULL,LocalBuffer,sizeof(LocalBuffer)))
  3620.     {
  3621.         ZTransmit(FileRequester->fr_ArgList,FileRequester->fr_NumArgs,TextMode);
  3622.  
  3623.         FreeAslRequest(FileRequester);
  3624.     }
  3625. }
  3626.  
  3627. VOID
  3628. InternalZModemTextUpload()
  3629. {
  3630.     InternalZModemUpload(TRUE);
  3631. }
  3632.  
  3633. VOID
  3634. InternalZModemBinaryUpload()
  3635. {
  3636.     InternalZModemUpload(FALSE);
  3637. }
  3638.  
  3639. STATIC STRPTR STACKARGS SAVE_DS
  3640. TimeoutDispFunc(struct Gadget *UnusedGadget,UWORD Level)
  3641. {
  3642.     STATIC UBYTE LocalBuffer[40];
  3643.  
  3644.     FormatTime(LocalBuffer,sizeof(LocalBuffer),Level / (60 * 60),(Level / 60) % 60,Level % 60);
  3645.  
  3646.     return(LocalBuffer);
  3647. }
  3648.  
  3649. STATIC STRPTR STACKARGS SAVE_DS
  3650. FrameDispFunc(struct Gadget *UnusedGadget,UWORD Level)
  3651. {
  3652.     if(Level > 0)
  3653.     {
  3654.         STATIC UBYTE LocalBuffer[40];
  3655.  
  3656.         LimitedSPrintf(sizeof(LocalBuffer),LocalBuffer,ConvNumber,Level);
  3657.         LimitedStrcat(sizeof(LocalBuffer),LocalBuffer," Bytes");
  3658.  
  3659.         return(LocalBuffer);
  3660.     }
  3661.     else
  3662.         return((STRPTR)"« Streaming »");
  3663. }
  3664.  
  3665. VOID
  3666. ZSettings()
  3667. {
  3668.     LayoutHandle *Handle;
  3669.  
  3670.     if(Handle = LT_CreateHandleTagList(Window->WScreen,NULL))
  3671.     {
  3672.         enum { GAD_Ok=1000 };
  3673.  
  3674.         LT_New(Handle,
  3675.             LA_Type,VERTICAL_KIND,
  3676.         TAG_DONE);
  3677.         {
  3678.             LT_New(Handle,
  3679.                 LA_Type,VERTICAL_KIND,
  3680.             TAG_DONE);
  3681.             {
  3682.                 LT_New(Handle,
  3683.                     LA_Type,        CHECKBOX_KIND,
  3684.                     LA_LabelText,    "Escape control characters",
  3685.                     LA_BOOL,        &ZEscapeCtl,
  3686.                 TAG_DONE);
  3687.  
  3688.                 LT_New(Handle,
  3689.                     LA_Type,        CHECKBOX_KIND,
  3690.                     LA_LabelText,    "Clobber existing files",
  3691.                     LA_BOOL,        &ZOverwrite,
  3692.                 TAG_DONE);
  3693.  
  3694.                 LT_New(Handle,
  3695.                     LA_Type,        SliderType,
  3696.                     LA_LabelText,    "Receive timeout",
  3697.                     LA_Chars,        20,
  3698.                     GTSL_Min,        1,
  3699.                     GTSL_Max,        12 * 60 * 60,
  3700.                     GTSL_DispFunc,    TimeoutDispFunc,
  3701.                     GTSL_LevelFormat,"%s h",
  3702.                     LA_LONG,        &ZTimeout,
  3703.                 TAG_DONE);
  3704.  
  3705.                 LT_New(Handle,
  3706.                     LA_Type,        SliderType,
  3707.                     LA_LabelText,    "Retry limit",
  3708.                     GTSL_Min,        0,
  3709.                     GTSL_Max,        1000,
  3710.                     LA_LONG,        &ZRetries,
  3711.                 TAG_DONE);
  3712.  
  3713.                 LT_New(Handle,
  3714.                     LA_Type,        SliderType,
  3715.                     LA_LabelText,    "Error limit",
  3716.                     GTSL_Min,        0,
  3717.                     GTSL_Max,        1000,
  3718.                     LA_LONG,        &ZErrors,
  3719.                 TAG_DONE);
  3720.  
  3721.                 LT_New(Handle,
  3722.                     LA_Type,        SliderType,
  3723.                     LA_LabelText,    "Frame size",
  3724.                     GTSL_Min,        0,
  3725.                     GTSL_Max,        1024,
  3726.                     LA_LONG,        &ZWindow,
  3727.                     GTSL_DispFunc,    FrameDispFunc,
  3728.                     GTSL_LevelFormat,"%s",
  3729.                 TAG_DONE);
  3730.  
  3731.                 LT_New(Handle,
  3732.                     LA_Type,        CHECKBOX_KIND,
  3733.                     LA_LabelText,    "Keep partial files",
  3734.                     LA_BOOL,        &ZKeepPartial,
  3735.                 TAG_DONE);
  3736.  
  3737.                 LT_New(Handle,
  3738.                     LA_Type,        CHECKBOX_KIND,
  3739.                     LA_LabelText,    "Delete files after sending",
  3740.                     LA_BOOL,        &ZDeleteAfterSending,
  3741.                 TAG_DONE);
  3742.  
  3743.                 LT_New(Handle,
  3744.                     LA_Type,        CHECKBOX_KIND,
  3745.                     LA_LabelText,    "Mark files as archived after sending",
  3746.                     LA_BOOL,        &ZProtectAfterSending,
  3747.                 TAG_DONE);
  3748.  
  3749.                 LT_New(Handle,
  3750.                     LA_Type,        CHECKBOX_KIND,
  3751.                     LA_LabelText,    "Do not send archived files",
  3752.                     LA_BOOL,        &ZSkipIfArchived,
  3753.                 TAG_DONE);
  3754.  
  3755.                 LT_New(Handle,
  3756.                     LA_Type,        CHECKBOX_KIND,
  3757.                     LA_LabelText,    "Send full path names",
  3758.                     LA_BOOL,        &ZSendFullPath,
  3759.                 TAG_DONE);
  3760.  
  3761.                 LT_New(Handle,
  3762.                     LA_Type,        CHECKBOX_KIND,
  3763.                     LA_LabelText,    "Use full received path names",
  3764.                     LA_BOOL,        &ZUseFullPath,
  3765.                 TAG_DONE);
  3766.  
  3767.                 LT_New(Handle,
  3768.                     LA_Type,        CHECKBOX_KIND,
  3769.                     LA_LabelText,    "Overwrite files if newer",
  3770.                     LA_BOOL,        &ZTransferIfNewer,
  3771.                 TAG_DONE);
  3772.  
  3773.                 LT_New(Handle,
  3774.                     LA_Type,        CHECKBOX_KIND,
  3775.                     LA_LabelText,    "Overwrite files if size is different",
  3776.                     LA_BOOL,        &ZTransferIfSize,
  3777.                 TAG_DONE);
  3778.  
  3779.                 LT_EndGroup(Handle);
  3780.             }
  3781.  
  3782.             LT_New(Handle,
  3783.                 LA_Type,HORIZONTAL_KIND,
  3784.             TAG_DONE);
  3785.             {
  3786.                 LT_New(Handle,
  3787.                     LA_Type,        XBAR_KIND,
  3788.                     LAXB_FullWidth,    TRUE,
  3789.                 TAG_DONE);
  3790.  
  3791.                 LT_EndGroup(Handle);
  3792.             }
  3793.  
  3794.             LT_New(Handle,
  3795.                 LA_Type,HORIZONTAL_KIND,
  3796.             TAG_DONE);
  3797.             {
  3798.                 LT_New(Handle,
  3799.                     LA_Type,        BUTTON_KIND,
  3800.                     LA_LabelText,    "Ok",
  3801.                     LA_ID,            GAD_Ok,
  3802.                     LABT_ExtraFat,    TRUE,
  3803.                     LABT_ReturnKey,    TRUE,
  3804.                 TAG_DONE);
  3805.  
  3806.                 LT_EndGroup(Handle);
  3807.             }
  3808.  
  3809.             LT_EndGroup(Handle);
  3810.         }
  3811.  
  3812.         if(LT_Build(Handle,
  3813.             LAWN_TitleText,    "Internal ZModem settings",
  3814.             LAWN_Zoom,        TRUE,
  3815.             LAWN_Parent,    Window,
  3816.             WA_DepthGadget,    TRUE,
  3817.             WA_DragBar,        TRUE,
  3818.             WA_RMBTrap,        TRUE,
  3819.             WA_Activate,    TRUE,
  3820.         TAG_DONE))
  3821.         {
  3822.             struct IntuiMessage *Message;
  3823.             ULONG MsgClass;
  3824.             struct Gadget *MsgGadget;
  3825.             BOOL Done;
  3826.  
  3827.             Done = FALSE;
  3828.  
  3829.             do
  3830.             {
  3831.                 WaitPort(Handle->Window->UserPort);
  3832.  
  3833.                 while(Message = LT_GetIMsg(Handle))
  3834.                 {
  3835.                     MsgClass    = Message->Class;
  3836.                     MsgGadget    = (struct Gadget *)Message->IAddress;
  3837.  
  3838.                     LT_ReplyIMsg(Message);
  3839.  
  3840.                     if(MsgClass == IDCMP_GADGETUP && MsgGadget->GadgetID == GAD_Ok)
  3841.                         Done = TRUE;
  3842.                 }
  3843.             }
  3844.             while(!Done);
  3845.         }
  3846.  
  3847.         LT_DeleteHandle(Handle);
  3848.     }
  3849. }
  3850.  
  3851. #endif    /* BUILTIN_ZMODEM */
  3852.